roojs-ui.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13
14 /*
15  * These classes are derivatives of the similarly named classes in the YUI Library.
16  * The original license:
17  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18  * Code licensed under the BSD License:
19  * http://developer.yahoo.net/yui/license.txt
20  */
21
22 (function() {
23
24 var Event=Roo.EventManager;
25 var Dom=Roo.lib.Dom;
26
27 /**
28  * @class Roo.dd.DragDrop
29  * @extends Roo.util.Observable
30  * Defines the interface and base operation of items that that can be
31  * dragged or can be drop targets.  It was designed to be extended, overriding
32  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
33  * Up to three html elements can be associated with a DragDrop instance:
34  * <ul>
35  * <li>linked element: the element that is passed into the constructor.
36  * This is the element which defines the boundaries for interaction with
37  * other DragDrop objects.</li>
38  * <li>handle element(s): The drag operation only occurs if the element that
39  * was clicked matches a handle element.  By default this is the linked
40  * element, but there are times that you will want only a portion of the
41  * linked element to initiate the drag operation, and the setHandleElId()
42  * method provides a way to define this.</li>
43  * <li>drag element: this represents the element that would be moved along
44  * with the cursor during a drag operation.  By default, this is the linked
45  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
46  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
47  * </li>
48  * </ul>
49  * This class should not be instantiated until the onload event to ensure that
50  * the associated elements are available.
51  * The following would define a DragDrop obj that would interact with any
52  * other DragDrop obj in the "group1" group:
53  * <pre>
54  *  dd = new Roo.dd.DragDrop("div1", "group1");
55  * </pre>
56  * Since none of the event handlers have been implemented, nothing would
57  * actually happen if you were to run the code above.  Normally you would
58  * override this class or one of the default implementations, but you can
59  * also override the methods you want on an instance of the class...
60  * <pre>
61  *  dd.onDragDrop = function(e, id) {
62  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
63  *  }
64  * </pre>
65  * @constructor
66  * @param {String} id of the element that is linked to this instance
67  * @param {String} sGroup the group of related DragDrop objects
68  * @param {object} config an object containing configurable attributes
69  *                Valid properties for DragDrop:
70  *                    padding, isTarget, maintainOffset, primaryButtonOnly
71  */
72 Roo.dd.DragDrop = function(id, sGroup, config) {
73     if (id) {
74         this.init(id, sGroup, config);
75     }
76     
77 };
78
79 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
80
81     /**
82      * The id of the element associated with this object.  This is what we
83      * refer to as the "linked element" because the size and position of
84      * this element is used to determine when the drag and drop objects have
85      * interacted.
86      * @property id
87      * @type String
88      */
89     id: null,
90
91     /**
92      * Configuration attributes passed into the constructor
93      * @property config
94      * @type object
95      */
96     config: null,
97
98     /**
99      * The id of the element that will be dragged.  By default this is same
100      * as the linked element , but could be changed to another element. Ex:
101      * Roo.dd.DDProxy
102      * @property dragElId
103      * @type String
104      * @private
105      */
106     dragElId: null,
107
108     /**
109      * the id of the element that initiates the drag operation.  By default
110      * this is the linked element, but could be changed to be a child of this
111      * element.  This lets us do things like only starting the drag when the
112      * header element within the linked html element is clicked.
113      * @property handleElId
114      * @type String
115      * @private
116      */
117     handleElId: null,
118
119     /**
120      * An associative array of HTML tags that will be ignored if clicked.
121      * @property invalidHandleTypes
122      * @type {string: string}
123      */
124     invalidHandleTypes: null,
125
126     /**
127      * An associative array of ids for elements that will be ignored if clicked
128      * @property invalidHandleIds
129      * @type {string: string}
130      */
131     invalidHandleIds: null,
132
133     /**
134      * An indexted array of css class names for elements that will be ignored
135      * if clicked.
136      * @property invalidHandleClasses
137      * @type string[]
138      */
139     invalidHandleClasses: null,
140
141     /**
142      * The linked element's absolute X position at the time the drag was
143      * started
144      * @property startPageX
145      * @type int
146      * @private
147      */
148     startPageX: 0,
149
150     /**
151      * The linked element's absolute X position at the time the drag was
152      * started
153      * @property startPageY
154      * @type int
155      * @private
156      */
157     startPageY: 0,
158
159     /**
160      * The group defines a logical collection of DragDrop objects that are
161      * related.  Instances only get events when interacting with other
162      * DragDrop object in the same group.  This lets us define multiple
163      * groups using a single DragDrop subclass if we want.
164      * @property groups
165      * @type {string: string}
166      */
167     groups: null,
168
169     /**
170      * Individual drag/drop instances can be locked.  This will prevent
171      * onmousedown start drag.
172      * @property locked
173      * @type boolean
174      * @private
175      */
176     locked: false,
177
178     /**
179      * Lock this instance
180      * @method lock
181      */
182     lock: function() { this.locked = true; },
183
184     /**
185      * Unlock this instace
186      * @method unlock
187      */
188     unlock: function() { this.locked = false; },
189
190     /**
191      * By default, all insances can be a drop target.  This can be disabled by
192      * setting isTarget to false.
193      * @method isTarget
194      * @type boolean
195      */
196     isTarget: true,
197
198     /**
199      * The padding configured for this drag and drop object for calculating
200      * the drop zone intersection with this object.
201      * @method padding
202      * @type int[]
203      */
204     padding: null,
205
206     /**
207      * Cached reference to the linked element
208      * @property _domRef
209      * @private
210      */
211     _domRef: null,
212
213     /**
214      * Internal typeof flag
215      * @property __ygDragDrop
216      * @private
217      */
218     __ygDragDrop: true,
219
220     /**
221      * Set to true when horizontal contraints are applied
222      * @property constrainX
223      * @type boolean
224      * @private
225      */
226     constrainX: false,
227
228     /**
229      * Set to true when vertical contraints are applied
230      * @property constrainY
231      * @type boolean
232      * @private
233      */
234     constrainY: false,
235
236     /**
237      * The left constraint
238      * @property minX
239      * @type int
240      * @private
241      */
242     minX: 0,
243
244     /**
245      * The right constraint
246      * @property maxX
247      * @type int
248      * @private
249      */
250     maxX: 0,
251
252     /**
253      * The up constraint
254      * @property minY
255      * @type int
256      * @type int
257      * @private
258      */
259     minY: 0,
260
261     /**
262      * The down constraint
263      * @property maxY
264      * @type int
265      * @private
266      */
267     maxY: 0,
268
269     /**
270      * Maintain offsets when we resetconstraints.  Set to true when you want
271      * the position of the element relative to its parent to stay the same
272      * when the page changes
273      *
274      * @property maintainOffset
275      * @type boolean
276      */
277     maintainOffset: false,
278
279     /**
280      * Array of pixel locations the element will snap to if we specified a
281      * horizontal graduation/interval.  This array is generated automatically
282      * when you define a tick interval.
283      * @property xTicks
284      * @type int[]
285      */
286     xTicks: null,
287
288     /**
289      * Array of pixel locations the element will snap to if we specified a
290      * vertical graduation/interval.  This array is generated automatically
291      * when you define a tick interval.
292      * @property yTicks
293      * @type int[]
294      */
295     yTicks: null,
296
297     /**
298      * By default the drag and drop instance will only respond to the primary
299      * button click (left button for a right-handed mouse).  Set to true to
300      * allow drag and drop to start with any mouse click that is propogated
301      * by the browser
302      * @property primaryButtonOnly
303      * @type boolean
304      */
305     primaryButtonOnly: true,
306
307     /**
308      * The availabe property is false until the linked dom element is accessible.
309      * @property available
310      * @type boolean
311      */
312     available: false,
313
314     /**
315      * By default, drags can only be initiated if the mousedown occurs in the
316      * region the linked element is.  This is done in part to work around a
317      * bug in some browsers that mis-report the mousedown if the previous
318      * mouseup happened outside of the window.  This property is set to true
319      * if outer handles are defined.
320      *
321      * @property hasOuterHandles
322      * @type boolean
323      * @default false
324      */
325     hasOuterHandles: false,
326
327     /**
328      * Code that executes immediately before the startDrag event
329      * @method b4StartDrag
330      * @private
331      */
332     b4StartDrag: function(x, y) { },
333
334     /**
335      * Abstract method called after a drag/drop object is clicked
336      * and the drag or mousedown time thresholds have beeen met.
337      * @method startDrag
338      * @param {int} X click location
339      * @param {int} Y click location
340      */
341     startDrag: function(x, y) { /* override this */ },
342
343     /**
344      * Code that executes immediately before the onDrag event
345      * @method b4Drag
346      * @private
347      */
348     b4Drag: function(e) { },
349
350     /**
351      * Abstract method called during the onMouseMove event while dragging an
352      * object.
353      * @method onDrag
354      * @param {Event} e the mousemove event
355      */
356     onDrag: function(e) { /* override this */ },
357
358     /**
359      * Abstract method called when this element fist begins hovering over
360      * another DragDrop obj
361      * @method onDragEnter
362      * @param {Event} e the mousemove event
363      * @param {String|DragDrop[]} id In POINT mode, the element
364      * id this is hovering over.  In INTERSECT mode, an array of one or more
365      * dragdrop items being hovered over.
366      */
367     onDragEnter: function(e, id) { /* override this */ },
368
369     /**
370      * Code that executes immediately before the onDragOver event
371      * @method b4DragOver
372      * @private
373      */
374     b4DragOver: function(e) { },
375
376     /**
377      * Abstract method called when this element is hovering over another
378      * DragDrop obj
379      * @method onDragOver
380      * @param {Event} e the mousemove event
381      * @param {String|DragDrop[]} id In POINT mode, the element
382      * id this is hovering over.  In INTERSECT mode, an array of dd items
383      * being hovered over.
384      */
385     onDragOver: function(e, id) { /* override this */ },
386
387     /**
388      * Code that executes immediately before the onDragOut event
389      * @method b4DragOut
390      * @private
391      */
392     b4DragOut: function(e) { },
393
394     /**
395      * Abstract method called when we are no longer hovering over an element
396      * @method onDragOut
397      * @param {Event} e the mousemove event
398      * @param {String|DragDrop[]} id In POINT mode, the element
399      * id this was hovering over.  In INTERSECT mode, an array of dd items
400      * that the mouse is no longer over.
401      */
402     onDragOut: function(e, id) { /* override this */ },
403
404     /**
405      * Code that executes immediately before the onDragDrop event
406      * @method b4DragDrop
407      * @private
408      */
409     b4DragDrop: function(e) { },
410
411     /**
412      * Abstract method called when this item is dropped on another DragDrop
413      * obj
414      * @method onDragDrop
415      * @param {Event} e the mouseup event
416      * @param {String|DragDrop[]} id In POINT mode, the element
417      * id this was dropped on.  In INTERSECT mode, an array of dd items this
418      * was dropped on.
419      */
420     onDragDrop: function(e, id) { /* override this */ },
421
422     /**
423      * Abstract method called when this item is dropped on an area with no
424      * drop target
425      * @method onInvalidDrop
426      * @param {Event} e the mouseup event
427      */
428     onInvalidDrop: function(e) { /* override this */ },
429
430     /**
431      * Code that executes immediately before the endDrag event
432      * @method b4EndDrag
433      * @private
434      */
435     b4EndDrag: function(e) { },
436
437     /**
438      * Fired when we are done dragging the object
439      * @method endDrag
440      * @param {Event} e the mouseup event
441      */
442     endDrag: function(e) { /* override this */ },
443
444     /**
445      * Code executed immediately before the onMouseDown event
446      * @method b4MouseDown
447      * @param {Event} e the mousedown event
448      * @private
449      */
450     b4MouseDown: function(e) {  },
451
452     /**
453      * Event handler that fires when a drag/drop obj gets a mousedown
454      * @method onMouseDown
455      * @param {Event} e the mousedown event
456      */
457     onMouseDown: function(e) { /* override this */ },
458
459     /**
460      * Event handler that fires when a drag/drop obj gets a mouseup
461      * @method onMouseUp
462      * @param {Event} e the mouseup event
463      */
464     onMouseUp: function(e) { /* override this */ },
465
466     /**
467      * Override the onAvailable method to do what is needed after the initial
468      * position was determined.
469      * @method onAvailable
470      */
471     onAvailable: function () {
472     },
473
474     /*
475      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
476      * @type Object
477      */
478     defaultPadding : {left:0, right:0, top:0, bottom:0},
479
480     /*
481      * Initializes the drag drop object's constraints to restrict movement to a certain element.
482  *
483  * Usage:
484  <pre><code>
485  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
486                 { dragElId: "existingProxyDiv" });
487  dd.startDrag = function(){
488      this.constrainTo("parent-id");
489  };
490  </code></pre>
491  * Or you can initalize it using the {@link Roo.Element} object:
492  <pre><code>
493  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
494      startDrag : function(){
495          this.constrainTo("parent-id");
496      }
497  });
498  </code></pre>
499      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
500      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
501      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
502      * an object containing the sides to pad. For example: {right:10, bottom:10}
503      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
504      */
505     constrainTo : function(constrainTo, pad, inContent){
506         if(typeof pad == "number"){
507             pad = {left: pad, right:pad, top:pad, bottom:pad};
508         }
509         pad = pad || this.defaultPadding;
510         var b = Roo.get(this.getEl()).getBox();
511         var ce = Roo.get(constrainTo);
512         var s = ce.getScroll();
513         var c, cd = ce.dom;
514         if(cd == document.body){
515             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
516         }else{
517             xy = ce.getXY();
518             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
519         }
520
521
522         var topSpace = b.y - c.y;
523         var leftSpace = b.x - c.x;
524
525         this.resetConstraints();
526         this.setXConstraint(leftSpace - (pad.left||0), // left
527                 c.width - leftSpace - b.width - (pad.right||0) //right
528         );
529         this.setYConstraint(topSpace - (pad.top||0), //top
530                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
531         );
532     },
533
534     /**
535      * Returns a reference to the linked element
536      * @method getEl
537      * @return {HTMLElement} the html element
538      */
539     getEl: function() {
540         if (!this._domRef) {
541             this._domRef = Roo.getDom(this.id);
542         }
543
544         return this._domRef;
545     },
546
547     /**
548      * Returns a reference to the actual element to drag.  By default this is
549      * the same as the html element, but it can be assigned to another
550      * element. An example of this can be found in Roo.dd.DDProxy
551      * @method getDragEl
552      * @return {HTMLElement} the html element
553      */
554     getDragEl: function() {
555         return Roo.getDom(this.dragElId);
556     },
557
558     /**
559      * Sets up the DragDrop object.  Must be called in the constructor of any
560      * Roo.dd.DragDrop subclass
561      * @method init
562      * @param id the id of the linked element
563      * @param {String} sGroup the group of related items
564      * @param {object} config configuration attributes
565      */
566     init: function(id, sGroup, config) {
567         this.initTarget(id, sGroup, config);
568         Event.on(this.id, "mousedown", this.handleMouseDown, this);
569         // Event.on(this.id, "selectstart", Event.preventDefault);
570     },
571
572     /**
573      * Initializes Targeting functionality only... the object does not
574      * get a mousedown handler.
575      * @method initTarget
576      * @param id the id of the linked element
577      * @param {String} sGroup the group of related items
578      * @param {object} config configuration attributes
579      */
580     initTarget: function(id, sGroup, config) {
581
582         // configuration attributes
583         this.config = config || {};
584
585         // create a local reference to the drag and drop manager
586         this.DDM = Roo.dd.DDM;
587         // initialize the groups array
588         this.groups = {};
589
590         // assume that we have an element reference instead of an id if the
591         // parameter is not a string
592         if (typeof id !== "string") {
593             id = Roo.id(id);
594         }
595
596         // set the id
597         this.id = id;
598
599         // add to an interaction group
600         this.addToGroup((sGroup) ? sGroup : "default");
601
602         // We don't want to register this as the handle with the manager
603         // so we just set the id rather than calling the setter.
604         this.handleElId = id;
605
606         // the linked element is the element that gets dragged by default
607         this.setDragElId(id);
608
609         // by default, clicked anchors will not start drag operations.
610         this.invalidHandleTypes = { A: "A" };
611         this.invalidHandleIds = {};
612         this.invalidHandleClasses = [];
613
614         this.applyConfig();
615
616         this.handleOnAvailable();
617     },
618
619     /**
620      * Applies the configuration parameters that were passed into the constructor.
621      * This is supposed to happen at each level through the inheritance chain.  So
622      * a DDProxy implentation will execute apply config on DDProxy, DD, and
623      * DragDrop in order to get all of the parameters that are available in
624      * each object.
625      * @method applyConfig
626      */
627     applyConfig: function() {
628
629         // configurable properties:
630         //    padding, isTarget, maintainOffset, primaryButtonOnly
631         this.padding           = this.config.padding || [0, 0, 0, 0];
632         this.isTarget          = (this.config.isTarget !== false);
633         this.maintainOffset    = (this.config.maintainOffset);
634         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
635
636     },
637
638     /**
639      * Executed when the linked element is available
640      * @method handleOnAvailable
641      * @private
642      */
643     handleOnAvailable: function() {
644         this.available = true;
645         this.resetConstraints();
646         this.onAvailable();
647     },
648
649      /**
650      * Configures the padding for the target zone in px.  Effectively expands
651      * (or reduces) the virtual object size for targeting calculations.
652      * Supports css-style shorthand; if only one parameter is passed, all sides
653      * will have that padding, and if only two are passed, the top and bottom
654      * will have the first param, the left and right the second.
655      * @method setPadding
656      * @param {int} iTop    Top pad
657      * @param {int} iRight  Right pad
658      * @param {int} iBot    Bot pad
659      * @param {int} iLeft   Left pad
660      */
661     setPadding: function(iTop, iRight, iBot, iLeft) {
662         // this.padding = [iLeft, iRight, iTop, iBot];
663         if (!iRight && 0 !== iRight) {
664             this.padding = [iTop, iTop, iTop, iTop];
665         } else if (!iBot && 0 !== iBot) {
666             this.padding = [iTop, iRight, iTop, iRight];
667         } else {
668             this.padding = [iTop, iRight, iBot, iLeft];
669         }
670     },
671
672     /**
673      * Stores the initial placement of the linked element.
674      * @method setInitialPosition
675      * @param {int} diffX   the X offset, default 0
676      * @param {int} diffY   the Y offset, default 0
677      */
678     setInitPosition: function(diffX, diffY) {
679         var el = this.getEl();
680
681         if (!this.DDM.verifyEl(el)) {
682             return;
683         }
684
685         var dx = diffX || 0;
686         var dy = diffY || 0;
687
688         var p = Dom.getXY( el );
689
690         this.initPageX = p[0] - dx;
691         this.initPageY = p[1] - dy;
692
693         this.lastPageX = p[0];
694         this.lastPageY = p[1];
695
696
697         this.setStartPosition(p);
698     },
699
700     /**
701      * Sets the start position of the element.  This is set when the obj
702      * is initialized, the reset when a drag is started.
703      * @method setStartPosition
704      * @param pos current position (from previous lookup)
705      * @private
706      */
707     setStartPosition: function(pos) {
708         var p = pos || Dom.getXY( this.getEl() );
709         this.deltaSetXY = null;
710
711         this.startPageX = p[0];
712         this.startPageY = p[1];
713     },
714
715     /**
716      * Add this instance to a group of related drag/drop objects.  All
717      * instances belong to at least one group, and can belong to as many
718      * groups as needed.
719      * @method addToGroup
720      * @param sGroup {string} the name of the group
721      */
722     addToGroup: function(sGroup) {
723         this.groups[sGroup] = true;
724         this.DDM.regDragDrop(this, sGroup);
725     },
726
727     /**
728      * Remove's this instance from the supplied interaction group
729      * @method removeFromGroup
730      * @param {string}  sGroup  The group to drop
731      */
732     removeFromGroup: function(sGroup) {
733         if (this.groups[sGroup]) {
734             delete this.groups[sGroup];
735         }
736
737         this.DDM.removeDDFromGroup(this, sGroup);
738     },
739
740     /**
741      * Allows you to specify that an element other than the linked element
742      * will be moved with the cursor during a drag
743      * @method setDragElId
744      * @param id {string} the id of the element that will be used to initiate the drag
745      */
746     setDragElId: function(id) {
747         this.dragElId = id;
748     },
749
750     /**
751      * Allows you to specify a child of the linked element that should be
752      * used to initiate the drag operation.  An example of this would be if
753      * you have a content div with text and links.  Clicking anywhere in the
754      * content area would normally start the drag operation.  Use this method
755      * to specify that an element inside of the content div is the element
756      * that starts the drag operation.
757      * @method setHandleElId
758      * @param id {string} the id of the element that will be used to
759      * initiate the drag.
760      */
761     setHandleElId: function(id) {
762         if (typeof id !== "string") {
763             id = Roo.id(id);
764         }
765         this.handleElId = id;
766         this.DDM.regHandle(this.id, id);
767     },
768
769     /**
770      * Allows you to set an element outside of the linked element as a drag
771      * handle
772      * @method setOuterHandleElId
773      * @param id the id of the element that will be used to initiate the drag
774      */
775     setOuterHandleElId: function(id) {
776         if (typeof id !== "string") {
777             id = Roo.id(id);
778         }
779         Event.on(id, "mousedown",
780                 this.handleMouseDown, this);
781         this.setHandleElId(id);
782
783         this.hasOuterHandles = true;
784     },
785
786     /**
787      * Remove all drag and drop hooks for this element
788      * @method unreg
789      */
790     unreg: function() {
791         Event.un(this.id, "mousedown",
792                 this.handleMouseDown);
793         this._domRef = null;
794         this.DDM._remove(this);
795     },
796
797     destroy : function(){
798         this.unreg();
799     },
800
801     /**
802      * Returns true if this instance is locked, or the drag drop mgr is locked
803      * (meaning that all drag/drop is disabled on the page.)
804      * @method isLocked
805      * @return {boolean} true if this obj or all drag/drop is locked, else
806      * false
807      */
808     isLocked: function() {
809         return (this.DDM.isLocked() || this.locked);
810     },
811
812     /**
813      * Fired when this object is clicked
814      * @method handleMouseDown
815      * @param {Event} e
816      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
817      * @private
818      */
819     handleMouseDown: function(e, oDD){
820         if (this.primaryButtonOnly && e.button != 0) {
821             return;
822         }
823
824         if (this.isLocked()) {
825             return;
826         }
827
828         this.DDM.refreshCache(this.groups);
829
830         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
831         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
832         } else {
833             if (this.clickValidator(e)) {
834
835                 // set the initial element position
836                 this.setStartPosition();
837
838
839                 this.b4MouseDown(e);
840                 this.onMouseDown(e);
841
842                 this.DDM.handleMouseDown(e, this);
843
844                 this.DDM.stopEvent(e);
845             } else {
846
847
848             }
849         }
850     },
851
852     clickValidator: function(e) {
853         var target = e.getTarget();
854         return ( this.isValidHandleChild(target) &&
855                     (this.id == this.handleElId ||
856                         this.DDM.handleWasClicked(target, this.id)) );
857     },
858
859     /**
860      * Allows you to specify a tag name that should not start a drag operation
861      * when clicked.  This is designed to facilitate embedding links within a
862      * drag handle that do something other than start the drag.
863      * @method addInvalidHandleType
864      * @param {string} tagName the type of element to exclude
865      */
866     addInvalidHandleType: function(tagName) {
867         var type = tagName.toUpperCase();
868         this.invalidHandleTypes[type] = type;
869     },
870
871     /**
872      * Lets you to specify an element id for a child of a drag handle
873      * that should not initiate a drag
874      * @method addInvalidHandleId
875      * @param {string} id the element id of the element you wish to ignore
876      */
877     addInvalidHandleId: function(id) {
878         if (typeof id !== "string") {
879             id = Roo.id(id);
880         }
881         this.invalidHandleIds[id] = id;
882     },
883
884     /**
885      * Lets you specify a css class of elements that will not initiate a drag
886      * @method addInvalidHandleClass
887      * @param {string} cssClass the class of the elements you wish to ignore
888      */
889     addInvalidHandleClass: function(cssClass) {
890         this.invalidHandleClasses.push(cssClass);
891     },
892
893     /**
894      * Unsets an excluded tag name set by addInvalidHandleType
895      * @method removeInvalidHandleType
896      * @param {string} tagName the type of element to unexclude
897      */
898     removeInvalidHandleType: function(tagName) {
899         var type = tagName.toUpperCase();
900         // this.invalidHandleTypes[type] = null;
901         delete this.invalidHandleTypes[type];
902     },
903
904     /**
905      * Unsets an invalid handle id
906      * @method removeInvalidHandleId
907      * @param {string} id the id of the element to re-enable
908      */
909     removeInvalidHandleId: function(id) {
910         if (typeof id !== "string") {
911             id = Roo.id(id);
912         }
913         delete this.invalidHandleIds[id];
914     },
915
916     /**
917      * Unsets an invalid css class
918      * @method removeInvalidHandleClass
919      * @param {string} cssClass the class of the element(s) you wish to
920      * re-enable
921      */
922     removeInvalidHandleClass: function(cssClass) {
923         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
924             if (this.invalidHandleClasses[i] == cssClass) {
925                 delete this.invalidHandleClasses[i];
926             }
927         }
928     },
929
930     /**
931      * Checks the tag exclusion list to see if this click should be ignored
932      * @method isValidHandleChild
933      * @param {HTMLElement} node the HTMLElement to evaluate
934      * @return {boolean} true if this is a valid tag type, false if not
935      */
936     isValidHandleChild: function(node) {
937
938         var valid = true;
939         // var n = (node.nodeName == "#text") ? node.parentNode : node;
940         var nodeName;
941         try {
942             nodeName = node.nodeName.toUpperCase();
943         } catch(e) {
944             nodeName = node.nodeName;
945         }
946         valid = valid && !this.invalidHandleTypes[nodeName];
947         valid = valid && !this.invalidHandleIds[node.id];
948
949         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
950             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
951         }
952
953
954         return valid;
955
956     },
957
958     /**
959      * Create the array of horizontal tick marks if an interval was specified
960      * in setXConstraint().
961      * @method setXTicks
962      * @private
963      */
964     setXTicks: function(iStartX, iTickSize) {
965         this.xTicks = [];
966         this.xTickSize = iTickSize;
967
968         var tickMap = {};
969
970         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
971             if (!tickMap[i]) {
972                 this.xTicks[this.xTicks.length] = i;
973                 tickMap[i] = true;
974             }
975         }
976
977         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
978             if (!tickMap[i]) {
979                 this.xTicks[this.xTicks.length] = i;
980                 tickMap[i] = true;
981             }
982         }
983
984         this.xTicks.sort(this.DDM.numericSort) ;
985     },
986
987     /**
988      * Create the array of vertical tick marks if an interval was specified in
989      * setYConstraint().
990      * @method setYTicks
991      * @private
992      */
993     setYTicks: function(iStartY, iTickSize) {
994         this.yTicks = [];
995         this.yTickSize = iTickSize;
996
997         var tickMap = {};
998
999         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
1000             if (!tickMap[i]) {
1001                 this.yTicks[this.yTicks.length] = i;
1002                 tickMap[i] = true;
1003             }
1004         }
1005
1006         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1007             if (!tickMap[i]) {
1008                 this.yTicks[this.yTicks.length] = i;
1009                 tickMap[i] = true;
1010             }
1011         }
1012
1013         this.yTicks.sort(this.DDM.numericSort) ;
1014     },
1015
1016     /**
1017      * By default, the element can be dragged any place on the screen.  Use
1018      * this method to limit the horizontal travel of the element.  Pass in
1019      * 0,0 for the parameters if you want to lock the drag to the y axis.
1020      * @method setXConstraint
1021      * @param {int} iLeft the number of pixels the element can move to the left
1022      * @param {int} iRight the number of pixels the element can move to the
1023      * right
1024      * @param {int} iTickSize optional parameter for specifying that the
1025      * element
1026      * should move iTickSize pixels at a time.
1027      */
1028     setXConstraint: function(iLeft, iRight, iTickSize) {
1029         this.leftConstraint = iLeft;
1030         this.rightConstraint = iRight;
1031
1032         this.minX = this.initPageX - iLeft;
1033         this.maxX = this.initPageX + iRight;
1034         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1035
1036         this.constrainX = true;
1037     },
1038
1039     /**
1040      * Clears any constraints applied to this instance.  Also clears ticks
1041      * since they can't exist independent of a constraint at this time.
1042      * @method clearConstraints
1043      */
1044     clearConstraints: function() {
1045         this.constrainX = false;
1046         this.constrainY = false;
1047         this.clearTicks();
1048     },
1049
1050     /**
1051      * Clears any tick interval defined for this instance
1052      * @method clearTicks
1053      */
1054     clearTicks: function() {
1055         this.xTicks = null;
1056         this.yTicks = null;
1057         this.xTickSize = 0;
1058         this.yTickSize = 0;
1059     },
1060
1061     /**
1062      * By default, the element can be dragged any place on the screen.  Set
1063      * this to limit the vertical travel of the element.  Pass in 0,0 for the
1064      * parameters if you want to lock the drag to the x axis.
1065      * @method setYConstraint
1066      * @param {int} iUp the number of pixels the element can move up
1067      * @param {int} iDown the number of pixels the element can move down
1068      * @param {int} iTickSize optional parameter for specifying that the
1069      * element should move iTickSize pixels at a time.
1070      */
1071     setYConstraint: function(iUp, iDown, iTickSize) {
1072         this.topConstraint = iUp;
1073         this.bottomConstraint = iDown;
1074
1075         this.minY = this.initPageY - iUp;
1076         this.maxY = this.initPageY + iDown;
1077         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1078
1079         this.constrainY = true;
1080
1081     },
1082
1083     /**
1084      * resetConstraints must be called if you manually reposition a dd element.
1085      * @method resetConstraints
1086      * @param {boolean} maintainOffset
1087      */
1088     resetConstraints: function() {
1089
1090
1091         // Maintain offsets if necessary
1092         if (this.initPageX || this.initPageX === 0) {
1093             // figure out how much this thing has moved
1094             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1095             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1096
1097             this.setInitPosition(dx, dy);
1098
1099         // This is the first time we have detected the element's position
1100         } else {
1101             this.setInitPosition();
1102         }
1103
1104         if (this.constrainX) {
1105             this.setXConstraint( this.leftConstraint,
1106                                  this.rightConstraint,
1107                                  this.xTickSize        );
1108         }
1109
1110         if (this.constrainY) {
1111             this.setYConstraint( this.topConstraint,
1112                                  this.bottomConstraint,
1113                                  this.yTickSize         );
1114         }
1115     },
1116
1117     /**
1118      * Normally the drag element is moved pixel by pixel, but we can specify
1119      * that it move a number of pixels at a time.  This method resolves the
1120      * location when we have it set up like this.
1121      * @method getTick
1122      * @param {int} val where we want to place the object
1123      * @param {int[]} tickArray sorted array of valid points
1124      * @return {int} the closest tick
1125      * @private
1126      */
1127     getTick: function(val, tickArray) {
1128
1129         if (!tickArray) {
1130             // If tick interval is not defined, it is effectively 1 pixel,
1131             // so we return the value passed to us.
1132             return val;
1133         } else if (tickArray[0] >= val) {
1134             // The value is lower than the first tick, so we return the first
1135             // tick.
1136             return tickArray[0];
1137         } else {
1138             for (var i=0, len=tickArray.length; i<len; ++i) {
1139                 var next = i + 1;
1140                 if (tickArray[next] && tickArray[next] >= val) {
1141                     var diff1 = val - tickArray[i];
1142                     var diff2 = tickArray[next] - val;
1143                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1144                 }
1145             }
1146
1147             // The value is larger than the last tick, so we return the last
1148             // tick.
1149             return tickArray[tickArray.length - 1];
1150         }
1151     },
1152
1153     /**
1154      * toString method
1155      * @method toString
1156      * @return {string} string representation of the dd obj
1157      */
1158     toString: function() {
1159         return ("DragDrop " + this.id);
1160     }
1161
1162 });
1163
1164 })();
1165 /*
1166  * Based on:
1167  * Ext JS Library 1.1.1
1168  * Copyright(c) 2006-2007, Ext JS, LLC.
1169  *
1170  * Originally Released Under LGPL - original licence link has changed is not relivant.
1171  *
1172  * Fork - LGPL
1173  * <script type="text/javascript">
1174  */
1175
1176
1177 /**
1178  * The drag and drop utility provides a framework for building drag and drop
1179  * applications.  In addition to enabling drag and drop for specific elements,
1180  * the drag and drop elements are tracked by the manager class, and the
1181  * interactions between the various elements are tracked during the drag and
1182  * the implementing code is notified about these important moments.
1183  */
1184
1185 // Only load the library once.  Rewriting the manager class would orphan
1186 // existing drag and drop instances.
1187 if (!Roo.dd.DragDropMgr) {
1188
1189 /**
1190  * @class Roo.dd.DragDropMgr
1191  * DragDropMgr is a singleton that tracks the element interaction for
1192  * all DragDrop items in the window.  Generally, you will not call
1193  * this class directly, but it does have helper methods that could
1194  * be useful in your DragDrop implementations.
1195  * @singleton
1196  */
1197 Roo.dd.DragDropMgr = function() {
1198
1199     var Event = Roo.EventManager;
1200
1201     return {
1202
1203         /**
1204          * Two dimensional Array of registered DragDrop objects.  The first
1205          * dimension is the DragDrop item group, the second the DragDrop
1206          * object.
1207          * @property ids
1208          * @type {string: string}
1209          * @private
1210          * @static
1211          */
1212         ids: {},
1213
1214         /**
1215          * Array of element ids defined as drag handles.  Used to determine
1216          * if the element that generated the mousedown event is actually the
1217          * handle and not the html element itself.
1218          * @property handleIds
1219          * @type {string: string}
1220          * @private
1221          * @static
1222          */
1223         handleIds: {},
1224
1225         /**
1226          * the DragDrop object that is currently being dragged
1227          * @property dragCurrent
1228          * @type DragDrop
1229          * @private
1230          * @static
1231          **/
1232         dragCurrent: null,
1233
1234         /**
1235          * the DragDrop object(s) that are being hovered over
1236          * @property dragOvers
1237          * @type Array
1238          * @private
1239          * @static
1240          */
1241         dragOvers: {},
1242
1243         /**
1244          * the X distance between the cursor and the object being dragged
1245          * @property deltaX
1246          * @type int
1247          * @private
1248          * @static
1249          */
1250         deltaX: 0,
1251
1252         /**
1253          * the Y distance between the cursor and the object being dragged
1254          * @property deltaY
1255          * @type int
1256          * @private
1257          * @static
1258          */
1259         deltaY: 0,
1260
1261         /**
1262          * Flag to determine if we should prevent the default behavior of the
1263          * events we define. By default this is true, but this can be set to
1264          * false if you need the default behavior (not recommended)
1265          * @property preventDefault
1266          * @type boolean
1267          * @static
1268          */
1269         preventDefault: true,
1270
1271         /**
1272          * Flag to determine if we should stop the propagation of the events
1273          * we generate. This is true by default but you may want to set it to
1274          * false if the html element contains other features that require the
1275          * mouse click.
1276          * @property stopPropagation
1277          * @type boolean
1278          * @static
1279          */
1280         stopPropagation: true,
1281
1282         /**
1283          * Internal flag that is set to true when drag and drop has been
1284          * intialized
1285          * @property initialized
1286          * @private
1287          * @static
1288          */
1289         initalized: false,
1290
1291         /**
1292          * All drag and drop can be disabled.
1293          * @property locked
1294          * @private
1295          * @static
1296          */
1297         locked: false,
1298
1299         /**
1300          * Called the first time an element is registered.
1301          * @method init
1302          * @private
1303          * @static
1304          */
1305         init: function() {
1306             this.initialized = true;
1307         },
1308
1309         /**
1310          * In point mode, drag and drop interaction is defined by the
1311          * location of the cursor during the drag/drop
1312          * @property POINT
1313          * @type int
1314          * @static
1315          */
1316         POINT: 0,
1317
1318         /**
1319          * In intersect mode, drag and drop interactio nis defined by the
1320          * overlap of two or more drag and drop objects.
1321          * @property INTERSECT
1322          * @type int
1323          * @static
1324          */
1325         INTERSECT: 1,
1326
1327         /**
1328          * The current drag and drop mode.  Default: POINT
1329          * @property mode
1330          * @type int
1331          * @static
1332          */
1333         mode: 0,
1334
1335         /**
1336          * Runs method on all drag and drop objects
1337          * @method _execOnAll
1338          * @private
1339          * @static
1340          */
1341         _execOnAll: function(sMethod, args) {
1342             for (var i in this.ids) {
1343                 for (var j in this.ids[i]) {
1344                     var oDD = this.ids[i][j];
1345                     if (! this.isTypeOfDD(oDD)) {
1346                         continue;
1347                     }
1348                     oDD[sMethod].apply(oDD, args);
1349                 }
1350             }
1351         },
1352
1353         /**
1354          * Drag and drop initialization.  Sets up the global event handlers
1355          * @method _onLoad
1356          * @private
1357          * @static
1358          */
1359         _onLoad: function() {
1360
1361             this.init();
1362
1363
1364             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
1365             Event.on(document, "mousemove", this.handleMouseMove, this, true);
1366             Event.on(window,   "unload",    this._onUnload, this, true);
1367             Event.on(window,   "resize",    this._onResize, this, true);
1368             // Event.on(window,   "mouseout",    this._test);
1369
1370         },
1371
1372         /**
1373          * Reset constraints on all drag and drop objs
1374          * @method _onResize
1375          * @private
1376          * @static
1377          */
1378         _onResize: function(e) {
1379             this._execOnAll("resetConstraints", []);
1380         },
1381
1382         /**
1383          * Lock all drag and drop functionality
1384          * @method lock
1385          * @static
1386          */
1387         lock: function() { this.locked = true; },
1388
1389         /**
1390          * Unlock all drag and drop functionality
1391          * @method unlock
1392          * @static
1393          */
1394         unlock: function() { this.locked = false; },
1395
1396         /**
1397          * Is drag and drop locked?
1398          * @method isLocked
1399          * @return {boolean} True if drag and drop is locked, false otherwise.
1400          * @static
1401          */
1402         isLocked: function() { return this.locked; },
1403
1404         /**
1405          * Location cache that is set for all drag drop objects when a drag is
1406          * initiated, cleared when the drag is finished.
1407          * @property locationCache
1408          * @private
1409          * @static
1410          */
1411         locationCache: {},
1412
1413         /**
1414          * Set useCache to false if you want to force object the lookup of each
1415          * drag and drop linked element constantly during a drag.
1416          * @property useCache
1417          * @type boolean
1418          * @static
1419          */
1420         useCache: true,
1421
1422         /**
1423          * The number of pixels that the mouse needs to move after the
1424          * mousedown before the drag is initiated.  Default=3;
1425          * @property clickPixelThresh
1426          * @type int
1427          * @static
1428          */
1429         clickPixelThresh: 3,
1430
1431         /**
1432          * The number of milliseconds after the mousedown event to initiate the
1433          * drag if we don't get a mouseup event. Default=1000
1434          * @property clickTimeThresh
1435          * @type int
1436          * @static
1437          */
1438         clickTimeThresh: 350,
1439
1440         /**
1441          * Flag that indicates that either the drag pixel threshold or the
1442          * mousdown time threshold has been met
1443          * @property dragThreshMet
1444          * @type boolean
1445          * @private
1446          * @static
1447          */
1448         dragThreshMet: false,
1449
1450         /**
1451          * Timeout used for the click time threshold
1452          * @property clickTimeout
1453          * @type Object
1454          * @private
1455          * @static
1456          */
1457         clickTimeout: null,
1458
1459         /**
1460          * The X position of the mousedown event stored for later use when a
1461          * drag threshold is met.
1462          * @property startX
1463          * @type int
1464          * @private
1465          * @static
1466          */
1467         startX: 0,
1468
1469         /**
1470          * The Y position of the mousedown event stored for later use when a
1471          * drag threshold is met.
1472          * @property startY
1473          * @type int
1474          * @private
1475          * @static
1476          */
1477         startY: 0,
1478
1479         /**
1480          * Each DragDrop instance must be registered with the DragDropMgr.
1481          * This is executed in DragDrop.init()
1482          * @method regDragDrop
1483          * @param {DragDrop} oDD the DragDrop object to register
1484          * @param {String} sGroup the name of the group this element belongs to
1485          * @static
1486          */
1487         regDragDrop: function(oDD, sGroup) {
1488             if (!this.initialized) { this.init(); }
1489
1490             if (!this.ids[sGroup]) {
1491                 this.ids[sGroup] = {};
1492             }
1493             this.ids[sGroup][oDD.id] = oDD;
1494         },
1495
1496         /**
1497          * Removes the supplied dd instance from the supplied group. Executed
1498          * by DragDrop.removeFromGroup, so don't call this function directly.
1499          * @method removeDDFromGroup
1500          * @private
1501          * @static
1502          */
1503         removeDDFromGroup: function(oDD, sGroup) {
1504             if (!this.ids[sGroup]) {
1505                 this.ids[sGroup] = {};
1506             }
1507
1508             var obj = this.ids[sGroup];
1509             if (obj && obj[oDD.id]) {
1510                 delete obj[oDD.id];
1511             }
1512         },
1513
1514         /**
1515          * Unregisters a drag and drop item.  This is executed in
1516          * DragDrop.unreg, use that method instead of calling this directly.
1517          * @method _remove
1518          * @private
1519          * @static
1520          */
1521         _remove: function(oDD) {
1522             for (var g in oDD.groups) {
1523                 if (g && this.ids[g][oDD.id]) {
1524                     delete this.ids[g][oDD.id];
1525                 }
1526             }
1527             delete this.handleIds[oDD.id];
1528         },
1529
1530         /**
1531          * Each DragDrop handle element must be registered.  This is done
1532          * automatically when executing DragDrop.setHandleElId()
1533          * @method regHandle
1534          * @param {String} sDDId the DragDrop id this element is a handle for
1535          * @param {String} sHandleId the id of the element that is the drag
1536          * handle
1537          * @static
1538          */
1539         regHandle: function(sDDId, sHandleId) {
1540             if (!this.handleIds[sDDId]) {
1541                 this.handleIds[sDDId] = {};
1542             }
1543             this.handleIds[sDDId][sHandleId] = sHandleId;
1544         },
1545
1546         /**
1547          * Utility function to determine if a given element has been
1548          * registered as a drag drop item.
1549          * @method isDragDrop
1550          * @param {String} id the element id to check
1551          * @return {boolean} true if this element is a DragDrop item,
1552          * false otherwise
1553          * @static
1554          */
1555         isDragDrop: function(id) {
1556             return ( this.getDDById(id) ) ? true : false;
1557         },
1558
1559         /**
1560          * Returns the drag and drop instances that are in all groups the
1561          * passed in instance belongs to.
1562          * @method getRelated
1563          * @param {DragDrop} p_oDD the obj to get related data for
1564          * @param {boolean} bTargetsOnly if true, only return targetable objs
1565          * @return {DragDrop[]} the related instances
1566          * @static
1567          */
1568         getRelated: function(p_oDD, bTargetsOnly) {
1569             var oDDs = [];
1570             for (var i in p_oDD.groups) {
1571                 for (j in this.ids[i]) {
1572                     var dd = this.ids[i][j];
1573                     if (! this.isTypeOfDD(dd)) {
1574                         continue;
1575                     }
1576                     if (!bTargetsOnly || dd.isTarget) {
1577                         oDDs[oDDs.length] = dd;
1578                     }
1579                 }
1580             }
1581
1582             return oDDs;
1583         },
1584
1585         /**
1586          * Returns true if the specified dd target is a legal target for
1587          * the specifice drag obj
1588          * @method isLegalTarget
1589          * @param {DragDrop} the drag obj
1590          * @param {DragDrop} the target
1591          * @return {boolean} true if the target is a legal target for the
1592          * dd obj
1593          * @static
1594          */
1595         isLegalTarget: function (oDD, oTargetDD) {
1596             var targets = this.getRelated(oDD, true);
1597             for (var i=0, len=targets.length;i<len;++i) {
1598                 if (targets[i].id == oTargetDD.id) {
1599                     return true;
1600                 }
1601             }
1602
1603             return false;
1604         },
1605
1606         /**
1607          * My goal is to be able to transparently determine if an object is
1608          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
1609          * returns "object", oDD.constructor.toString() always returns
1610          * "DragDrop" and not the name of the subclass.  So for now it just
1611          * evaluates a well-known variable in DragDrop.
1612          * @method isTypeOfDD
1613          * @param {Object} the object to evaluate
1614          * @return {boolean} true if typeof oDD = DragDrop
1615          * @static
1616          */
1617         isTypeOfDD: function (oDD) {
1618             return (oDD && oDD.__ygDragDrop);
1619         },
1620
1621         /**
1622          * Utility function to determine if a given element has been
1623          * registered as a drag drop handle for the given Drag Drop object.
1624          * @method isHandle
1625          * @param {String} id the element id to check
1626          * @return {boolean} true if this element is a DragDrop handle, false
1627          * otherwise
1628          * @static
1629          */
1630         isHandle: function(sDDId, sHandleId) {
1631             return ( this.handleIds[sDDId] &&
1632                             this.handleIds[sDDId][sHandleId] );
1633         },
1634
1635         /**
1636          * Returns the DragDrop instance for a given id
1637          * @method getDDById
1638          * @param {String} id the id of the DragDrop object
1639          * @return {DragDrop} the drag drop object, null if it is not found
1640          * @static
1641          */
1642         getDDById: function(id) {
1643             for (var i in this.ids) {
1644                 if (this.ids[i][id]) {
1645                     return this.ids[i][id];
1646                 }
1647             }
1648             return null;
1649         },
1650
1651         /**
1652          * Fired after a registered DragDrop object gets the mousedown event.
1653          * Sets up the events required to track the object being dragged
1654          * @method handleMouseDown
1655          * @param {Event} e the event
1656          * @param oDD the DragDrop object being dragged
1657          * @private
1658          * @static
1659          */
1660         handleMouseDown: function(e, oDD) {
1661             if(Roo.QuickTips){
1662                 Roo.QuickTips.disable();
1663             }
1664             this.currentTarget = e.getTarget();
1665
1666             this.dragCurrent = oDD;
1667
1668             var el = oDD.getEl();
1669
1670             // track start position
1671             this.startX = e.getPageX();
1672             this.startY = e.getPageY();
1673
1674             this.deltaX = this.startX - el.offsetLeft;
1675             this.deltaY = this.startY - el.offsetTop;
1676
1677             this.dragThreshMet = false;
1678
1679             this.clickTimeout = setTimeout(
1680                     function() {
1681                         var DDM = Roo.dd.DDM;
1682                         DDM.startDrag(DDM.startX, DDM.startY);
1683                     },
1684                     this.clickTimeThresh );
1685         },
1686
1687         /**
1688          * Fired when either the drag pixel threshol or the mousedown hold
1689          * time threshold has been met.
1690          * @method startDrag
1691          * @param x {int} the X position of the original mousedown
1692          * @param y {int} the Y position of the original mousedown
1693          * @static
1694          */
1695         startDrag: function(x, y) {
1696             clearTimeout(this.clickTimeout);
1697             if (this.dragCurrent) {
1698                 this.dragCurrent.b4StartDrag(x, y);
1699                 this.dragCurrent.startDrag(x, y);
1700             }
1701             this.dragThreshMet = true;
1702         },
1703
1704         /**
1705          * Internal function to handle the mouseup event.  Will be invoked
1706          * from the context of the document.
1707          * @method handleMouseUp
1708          * @param {Event} e the event
1709          * @private
1710          * @static
1711          */
1712         handleMouseUp: function(e) {
1713
1714             if(Roo.QuickTips){
1715                 Roo.QuickTips.enable();
1716             }
1717             if (! this.dragCurrent) {
1718                 return;
1719             }
1720
1721             clearTimeout(this.clickTimeout);
1722
1723             if (this.dragThreshMet) {
1724                 this.fireEvents(e, true);
1725             } else {
1726             }
1727
1728             this.stopDrag(e);
1729
1730             this.stopEvent(e);
1731         },
1732
1733         /**
1734          * Utility to stop event propagation and event default, if these
1735          * features are turned on.
1736          * @method stopEvent
1737          * @param {Event} e the event as returned by this.getEvent()
1738          * @static
1739          */
1740         stopEvent: function(e){
1741             if(this.stopPropagation) {
1742                 e.stopPropagation();
1743             }
1744
1745             if (this.preventDefault) {
1746                 e.preventDefault();
1747             }
1748         },
1749
1750         /**
1751          * Internal function to clean up event handlers after the drag
1752          * operation is complete
1753          * @method stopDrag
1754          * @param {Event} e the event
1755          * @private
1756          * @static
1757          */
1758         stopDrag: function(e) {
1759             // Fire the drag end event for the item that was dragged
1760             if (this.dragCurrent) {
1761                 if (this.dragThreshMet) {
1762                     this.dragCurrent.b4EndDrag(e);
1763                     this.dragCurrent.endDrag(e);
1764                 }
1765
1766                 this.dragCurrent.onMouseUp(e);
1767             }
1768
1769             this.dragCurrent = null;
1770             this.dragOvers = {};
1771         },
1772
1773         /**
1774          * Internal function to handle the mousemove event.  Will be invoked
1775          * from the context of the html element.
1776          *
1777          * @TODO figure out what we can do about mouse events lost when the
1778          * user drags objects beyond the window boundary.  Currently we can
1779          * detect this in internet explorer by verifying that the mouse is
1780          * down during the mousemove event.  Firefox doesn't give us the
1781          * button state on the mousemove event.
1782          * @method handleMouseMove
1783          * @param {Event} e the event
1784          * @private
1785          * @static
1786          */
1787         handleMouseMove: function(e) {
1788             if (! this.dragCurrent) {
1789                 return true;
1790             }
1791
1792             // var button = e.which || e.button;
1793
1794             // check for IE mouseup outside of page boundary
1795             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1796                 this.stopEvent(e);
1797                 return this.handleMouseUp(e);
1798             }
1799
1800             if (!this.dragThreshMet) {
1801                 var diffX = Math.abs(this.startX - e.getPageX());
1802                 var diffY = Math.abs(this.startY - e.getPageY());
1803                 if (diffX > this.clickPixelThresh ||
1804                             diffY > this.clickPixelThresh) {
1805                     this.startDrag(this.startX, this.startY);
1806                 }
1807             }
1808
1809             if (this.dragThreshMet) {
1810                 this.dragCurrent.b4Drag(e);
1811                 this.dragCurrent.onDrag(e);
1812                 if(!this.dragCurrent.moveOnly){
1813                     this.fireEvents(e, false);
1814                 }
1815             }
1816
1817             this.stopEvent(e);
1818
1819             return true;
1820         },
1821
1822         /**
1823          * Iterates over all of the DragDrop elements to find ones we are
1824          * hovering over or dropping on
1825          * @method fireEvents
1826          * @param {Event} e the event
1827          * @param {boolean} isDrop is this a drop op or a mouseover op?
1828          * @private
1829          * @static
1830          */
1831         fireEvents: function(e, isDrop) {
1832             var dc = this.dragCurrent;
1833
1834             // If the user did the mouse up outside of the window, we could
1835             // get here even though we have ended the drag.
1836             if (!dc || dc.isLocked()) {
1837                 return;
1838             }
1839
1840             var pt = e.getPoint();
1841
1842             // cache the previous dragOver array
1843             var oldOvers = [];
1844
1845             var outEvts   = [];
1846             var overEvts  = [];
1847             var dropEvts  = [];
1848             var enterEvts = [];
1849
1850             // Check to see if the object(s) we were hovering over is no longer
1851             // being hovered over so we can fire the onDragOut event
1852             for (var i in this.dragOvers) {
1853
1854                 var ddo = this.dragOvers[i];
1855
1856                 if (! this.isTypeOfDD(ddo)) {
1857                     continue;
1858                 }
1859
1860                 if (! this.isOverTarget(pt, ddo, this.mode)) {
1861                     outEvts.push( ddo );
1862                 }
1863
1864                 oldOvers[i] = true;
1865                 delete this.dragOvers[i];
1866             }
1867
1868             for (var sGroup in dc.groups) {
1869
1870                 if ("string" != typeof sGroup) {
1871                     continue;
1872                 }
1873
1874                 for (i in this.ids[sGroup]) {
1875                     var oDD = this.ids[sGroup][i];
1876                     if (! this.isTypeOfDD(oDD)) {
1877                         continue;
1878                     }
1879
1880                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1881                         if (this.isOverTarget(pt, oDD, this.mode)) {
1882                             // look for drop interactions
1883                             if (isDrop) {
1884                                 dropEvts.push( oDD );
1885                             // look for drag enter and drag over interactions
1886                             } else {
1887
1888                                 // initial drag over: dragEnter fires
1889                                 if (!oldOvers[oDD.id]) {
1890                                     enterEvts.push( oDD );
1891                                 // subsequent drag overs: dragOver fires
1892                                 } else {
1893                                     overEvts.push( oDD );
1894                                 }
1895
1896                                 this.dragOvers[oDD.id] = oDD;
1897                             }
1898                         }
1899                     }
1900                 }
1901             }
1902
1903             if (this.mode) {
1904                 if (outEvts.length) {
1905                     dc.b4DragOut(e, outEvts);
1906                     dc.onDragOut(e, outEvts);
1907                 }
1908
1909                 if (enterEvts.length) {
1910                     dc.onDragEnter(e, enterEvts);
1911                 }
1912
1913                 if (overEvts.length) {
1914                     dc.b4DragOver(e, overEvts);
1915                     dc.onDragOver(e, overEvts);
1916                 }
1917
1918                 if (dropEvts.length) {
1919                     dc.b4DragDrop(e, dropEvts);
1920                     dc.onDragDrop(e, dropEvts);
1921                 }
1922
1923             } else {
1924                 // fire dragout events
1925                 var len = 0;
1926                 for (i=0, len=outEvts.length; i<len; ++i) {
1927                     dc.b4DragOut(e, outEvts[i].id);
1928                     dc.onDragOut(e, outEvts[i].id);
1929                 }
1930
1931                 // fire enter events
1932                 for (i=0,len=enterEvts.length; i<len; ++i) {
1933                     // dc.b4DragEnter(e, oDD.id);
1934                     dc.onDragEnter(e, enterEvts[i].id);
1935                 }
1936
1937                 // fire over events
1938                 for (i=0,len=overEvts.length; i<len; ++i) {
1939                     dc.b4DragOver(e, overEvts[i].id);
1940                     dc.onDragOver(e, overEvts[i].id);
1941                 }
1942
1943                 // fire drop events
1944                 for (i=0, len=dropEvts.length; i<len; ++i) {
1945                     dc.b4DragDrop(e, dropEvts[i].id);
1946                     dc.onDragDrop(e, dropEvts[i].id);
1947                 }
1948
1949             }
1950
1951             // notify about a drop that did not find a target
1952             if (isDrop && !dropEvts.length) {
1953                 dc.onInvalidDrop(e);
1954             }
1955
1956         },
1957
1958         /**
1959          * Helper function for getting the best match from the list of drag
1960          * and drop objects returned by the drag and drop events when we are
1961          * in INTERSECT mode.  It returns either the first object that the
1962          * cursor is over, or the object that has the greatest overlap with
1963          * the dragged element.
1964          * @method getBestMatch
1965          * @param  {DragDrop[]} dds The array of drag and drop objects
1966          * targeted
1967          * @return {DragDrop}       The best single match
1968          * @static
1969          */
1970         getBestMatch: function(dds) {
1971             var winner = null;
1972             // Return null if the input is not what we expect
1973             //if (!dds || !dds.length || dds.length == 0) {
1974                // winner = null;
1975             // If there is only one item, it wins
1976             //} else if (dds.length == 1) {
1977
1978             var len = dds.length;
1979
1980             if (len == 1) {
1981                 winner = dds[0];
1982             } else {
1983                 // Loop through the targeted items
1984                 for (var i=0; i<len; ++i) {
1985                     var dd = dds[i];
1986                     // If the cursor is over the object, it wins.  If the
1987                     // cursor is over multiple matches, the first one we come
1988                     // to wins.
1989                     if (dd.cursorIsOver) {
1990                         winner = dd;
1991                         break;
1992                     // Otherwise the object with the most overlap wins
1993                     } else {
1994                         if (!winner ||
1995                             winner.overlap.getArea() < dd.overlap.getArea()) {
1996                             winner = dd;
1997                         }
1998                     }
1999                 }
2000             }
2001
2002             return winner;
2003         },
2004
2005         /**
2006          * Refreshes the cache of the top-left and bottom-right points of the
2007          * drag and drop objects in the specified group(s).  This is in the
2008          * format that is stored in the drag and drop instance, so typical
2009          * usage is:
2010          * <code>
2011          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2012          * </code>
2013          * Alternatively:
2014          * <code>
2015          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2016          * </code>
2017          * @TODO this really should be an indexed array.  Alternatively this
2018          * method could accept both.
2019          * @method refreshCache
2020          * @param {Object} groups an associative array of groups to refresh
2021          * @static
2022          */
2023         refreshCache: function(groups) {
2024             for (var sGroup in groups) {
2025                 if ("string" != typeof sGroup) {
2026                     continue;
2027                 }
2028                 for (var i in this.ids[sGroup]) {
2029                     var oDD = this.ids[sGroup][i];
2030
2031                     if (this.isTypeOfDD(oDD)) {
2032                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2033                         var loc = this.getLocation(oDD);
2034                         if (loc) {
2035                             this.locationCache[oDD.id] = loc;
2036                         } else {
2037                             delete this.locationCache[oDD.id];
2038                             // this will unregister the drag and drop object if
2039                             // the element is not in a usable state
2040                             // oDD.unreg();
2041                         }
2042                     }
2043                 }
2044             }
2045         },
2046
2047         /**
2048          * This checks to make sure an element exists and is in the DOM.  The
2049          * main purpose is to handle cases where innerHTML is used to remove
2050          * drag and drop objects from the DOM.  IE provides an 'unspecified
2051          * error' when trying to access the offsetParent of such an element
2052          * @method verifyEl
2053          * @param {HTMLElement} el the element to check
2054          * @return {boolean} true if the element looks usable
2055          * @static
2056          */
2057         verifyEl: function(el) {
2058             if (el) {
2059                 var parent;
2060                 if(Roo.isIE){
2061                     try{
2062                         parent = el.offsetParent;
2063                     }catch(e){}
2064                 }else{
2065                     parent = el.offsetParent;
2066                 }
2067                 if (parent) {
2068                     return true;
2069                 }
2070             }
2071
2072             return false;
2073         },
2074
2075         /**
2076          * Returns a Region object containing the drag and drop element's position
2077          * and size, including the padding configured for it
2078          * @method getLocation
2079          * @param {DragDrop} oDD the drag and drop object to get the
2080          *                       location for
2081          * @return {Roo.lib.Region} a Region object representing the total area
2082          *                             the element occupies, including any padding
2083          *                             the instance is configured for.
2084          * @static
2085          */
2086         getLocation: function(oDD) {
2087             if (! this.isTypeOfDD(oDD)) {
2088                 return null;
2089             }
2090
2091             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2092
2093             try {
2094                 pos= Roo.lib.Dom.getXY(el);
2095             } catch (e) { }
2096
2097             if (!pos) {
2098                 return null;
2099             }
2100
2101             x1 = pos[0];
2102             x2 = x1 + el.offsetWidth;
2103             y1 = pos[1];
2104             y2 = y1 + el.offsetHeight;
2105
2106             t = y1 - oDD.padding[0];
2107             r = x2 + oDD.padding[1];
2108             b = y2 + oDD.padding[2];
2109             l = x1 - oDD.padding[3];
2110
2111             return new Roo.lib.Region( t, r, b, l );
2112         },
2113
2114         /**
2115          * Checks the cursor location to see if it over the target
2116          * @method isOverTarget
2117          * @param {Roo.lib.Point} pt The point to evaluate
2118          * @param {DragDrop} oTarget the DragDrop object we are inspecting
2119          * @return {boolean} true if the mouse is over the target
2120          * @private
2121          * @static
2122          */
2123         isOverTarget: function(pt, oTarget, intersect) {
2124             // use cache if available
2125             var loc = this.locationCache[oTarget.id];
2126             if (!loc || !this.useCache) {
2127                 loc = this.getLocation(oTarget);
2128                 this.locationCache[oTarget.id] = loc;
2129
2130             }
2131
2132             if (!loc) {
2133                 return false;
2134             }
2135
2136             oTarget.cursorIsOver = loc.contains( pt );
2137
2138             // DragDrop is using this as a sanity check for the initial mousedown
2139             // in this case we are done.  In POINT mode, if the drag obj has no
2140             // contraints, we are also done. Otherwise we need to evaluate the
2141             // location of the target as related to the actual location of the
2142             // dragged element.
2143             var dc = this.dragCurrent;
2144             if (!dc || !dc.getTargetCoord ||
2145                     (!intersect && !dc.constrainX && !dc.constrainY)) {
2146                 return oTarget.cursorIsOver;
2147             }
2148
2149             oTarget.overlap = null;
2150
2151             // Get the current location of the drag element, this is the
2152             // location of the mouse event less the delta that represents
2153             // where the original mousedown happened on the element.  We
2154             // need to consider constraints and ticks as well.
2155             var pos = dc.getTargetCoord(pt.x, pt.y);
2156
2157             var el = dc.getDragEl();
2158             var curRegion = new Roo.lib.Region( pos.y,
2159                                                    pos.x + el.offsetWidth,
2160                                                    pos.y + el.offsetHeight,
2161                                                    pos.x );
2162
2163             var overlap = curRegion.intersect(loc);
2164
2165             if (overlap) {
2166                 oTarget.overlap = overlap;
2167                 return (intersect) ? true : oTarget.cursorIsOver;
2168             } else {
2169                 return false;
2170             }
2171         },
2172
2173         /**
2174          * unload event handler
2175          * @method _onUnload
2176          * @private
2177          * @static
2178          */
2179         _onUnload: function(e, me) {
2180             Roo.dd.DragDropMgr.unregAll();
2181         },
2182
2183         /**
2184          * Cleans up the drag and drop events and objects.
2185          * @method unregAll
2186          * @private
2187          * @static
2188          */
2189         unregAll: function() {
2190
2191             if (this.dragCurrent) {
2192                 this.stopDrag();
2193                 this.dragCurrent = null;
2194             }
2195
2196             this._execOnAll("unreg", []);
2197
2198             for (i in this.elementCache) {
2199                 delete this.elementCache[i];
2200             }
2201
2202             this.elementCache = {};
2203             this.ids = {};
2204         },
2205
2206         /**
2207          * A cache of DOM elements
2208          * @property elementCache
2209          * @private
2210          * @static
2211          */
2212         elementCache: {},
2213
2214         /**
2215          * Get the wrapper for the DOM element specified
2216          * @method getElWrapper
2217          * @param {String} id the id of the element to get
2218          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2219          * @private
2220          * @deprecated This wrapper isn't that useful
2221          * @static
2222          */
2223         getElWrapper: function(id) {
2224             var oWrapper = this.elementCache[id];
2225             if (!oWrapper || !oWrapper.el) {
2226                 oWrapper = this.elementCache[id] =
2227                     new this.ElementWrapper(Roo.getDom(id));
2228             }
2229             return oWrapper;
2230         },
2231
2232         /**
2233          * Returns the actual DOM element
2234          * @method getElement
2235          * @param {String} id the id of the elment to get
2236          * @return {Object} The element
2237          * @deprecated use Roo.getDom instead
2238          * @static
2239          */
2240         getElement: function(id) {
2241             return Roo.getDom(id);
2242         },
2243
2244         /**
2245          * Returns the style property for the DOM element (i.e.,
2246          * document.getElById(id).style)
2247          * @method getCss
2248          * @param {String} id the id of the elment to get
2249          * @return {Object} The style property of the element
2250          * @deprecated use Roo.getDom instead
2251          * @static
2252          */
2253         getCss: function(id) {
2254             var el = Roo.getDom(id);
2255             return (el) ? el.style : null;
2256         },
2257
2258         /**
2259          * Inner class for cached elements
2260          * @class DragDropMgr.ElementWrapper
2261          * @for DragDropMgr
2262          * @private
2263          * @deprecated
2264          */
2265         ElementWrapper: function(el) {
2266                 /**
2267                  * The element
2268                  * @property el
2269                  */
2270                 this.el = el || null;
2271                 /**
2272                  * The element id
2273                  * @property id
2274                  */
2275                 this.id = this.el && el.id;
2276                 /**
2277                  * A reference to the style property
2278                  * @property css
2279                  */
2280                 this.css = this.el && el.style;
2281             },
2282
2283         /**
2284          * Returns the X position of an html element
2285          * @method getPosX
2286          * @param el the element for which to get the position
2287          * @return {int} the X coordinate
2288          * @for DragDropMgr
2289          * @deprecated use Roo.lib.Dom.getX instead
2290          * @static
2291          */
2292         getPosX: function(el) {
2293             return Roo.lib.Dom.getX(el);
2294         },
2295
2296         /**
2297          * Returns the Y position of an html element
2298          * @method getPosY
2299          * @param el the element for which to get the position
2300          * @return {int} the Y coordinate
2301          * @deprecated use Roo.lib.Dom.getY instead
2302          * @static
2303          */
2304         getPosY: function(el) {
2305             return Roo.lib.Dom.getY(el);
2306         },
2307
2308         /**
2309          * Swap two nodes.  In IE, we use the native method, for others we
2310          * emulate the IE behavior
2311          * @method swapNode
2312          * @param n1 the first node to swap
2313          * @param n2 the other node to swap
2314          * @static
2315          */
2316         swapNode: function(n1, n2) {
2317             if (n1.swapNode) {
2318                 n1.swapNode(n2);
2319             } else {
2320                 var p = n2.parentNode;
2321                 var s = n2.nextSibling;
2322
2323                 if (s == n1) {
2324                     p.insertBefore(n1, n2);
2325                 } else if (n2 == n1.nextSibling) {
2326                     p.insertBefore(n2, n1);
2327                 } else {
2328                     n1.parentNode.replaceChild(n2, n1);
2329                     p.insertBefore(n1, s);
2330                 }
2331             }
2332         },
2333
2334         /**
2335          * Returns the current scroll position
2336          * @method getScroll
2337          * @private
2338          * @static
2339          */
2340         getScroll: function () {
2341             var t, l, dde=document.documentElement, db=document.body;
2342             if (dde && (dde.scrollTop || dde.scrollLeft)) {
2343                 t = dde.scrollTop;
2344                 l = dde.scrollLeft;
2345             } else if (db) {
2346                 t = db.scrollTop;
2347                 l = db.scrollLeft;
2348             } else {
2349
2350             }
2351             return { top: t, left: l };
2352         },
2353
2354         /**
2355          * Returns the specified element style property
2356          * @method getStyle
2357          * @param {HTMLElement} el          the element
2358          * @param {string}      styleProp   the style property
2359          * @return {string} The value of the style property
2360          * @deprecated use Roo.lib.Dom.getStyle
2361          * @static
2362          */
2363         getStyle: function(el, styleProp) {
2364             return Roo.fly(el).getStyle(styleProp);
2365         },
2366
2367         /**
2368          * Gets the scrollTop
2369          * @method getScrollTop
2370          * @return {int} the document's scrollTop
2371          * @static
2372          */
2373         getScrollTop: function () { return this.getScroll().top; },
2374
2375         /**
2376          * Gets the scrollLeft
2377          * @method getScrollLeft
2378          * @return {int} the document's scrollTop
2379          * @static
2380          */
2381         getScrollLeft: function () { return this.getScroll().left; },
2382
2383         /**
2384          * Sets the x/y position of an element to the location of the
2385          * target element.
2386          * @method moveToEl
2387          * @param {HTMLElement} moveEl      The element to move
2388          * @param {HTMLElement} targetEl    The position reference element
2389          * @static
2390          */
2391         moveToEl: function (moveEl, targetEl) {
2392             var aCoord = Roo.lib.Dom.getXY(targetEl);
2393             Roo.lib.Dom.setXY(moveEl, aCoord);
2394         },
2395
2396         /**
2397          * Numeric array sort function
2398          * @method numericSort
2399          * @static
2400          */
2401         numericSort: function(a, b) { return (a - b); },
2402
2403         /**
2404          * Internal counter
2405          * @property _timeoutCount
2406          * @private
2407          * @static
2408          */
2409         _timeoutCount: 0,
2410
2411         /**
2412          * Trying to make the load order less important.  Without this we get
2413          * an error if this file is loaded before the Event Utility.
2414          * @method _addListeners
2415          * @private
2416          * @static
2417          */
2418         _addListeners: function() {
2419             var DDM = Roo.dd.DDM;
2420             if ( Roo.lib.Event && document ) {
2421                 DDM._onLoad();
2422             } else {
2423                 if (DDM._timeoutCount > 2000) {
2424                 } else {
2425                     setTimeout(DDM._addListeners, 10);
2426                     if (document && document.body) {
2427                         DDM._timeoutCount += 1;
2428                     }
2429                 }
2430             }
2431         },
2432
2433         /**
2434          * Recursively searches the immediate parent and all child nodes for
2435          * the handle element in order to determine wheter or not it was
2436          * clicked.
2437          * @method handleWasClicked
2438          * @param node the html element to inspect
2439          * @static
2440          */
2441         handleWasClicked: function(node, id) {
2442             if (this.isHandle(id, node.id)) {
2443                 return true;
2444             } else {
2445                 // check to see if this is a text node child of the one we want
2446                 var p = node.parentNode;
2447
2448                 while (p) {
2449                     if (this.isHandle(id, p.id)) {
2450                         return true;
2451                     } else {
2452                         p = p.parentNode;
2453                     }
2454                 }
2455             }
2456
2457             return false;
2458         }
2459
2460     };
2461
2462 }();
2463
2464 // shorter alias, save a few bytes
2465 Roo.dd.DDM = Roo.dd.DragDropMgr;
2466 Roo.dd.DDM._addListeners();
2467
2468 }/*
2469  * Based on:
2470  * Ext JS Library 1.1.1
2471  * Copyright(c) 2006-2007, Ext JS, LLC.
2472  *
2473  * Originally Released Under LGPL - original licence link has changed is not relivant.
2474  *
2475  * Fork - LGPL
2476  * <script type="text/javascript">
2477  */
2478
2479 /**
2480  * @class Roo.dd.DD
2481  * A DragDrop implementation where the linked element follows the
2482  * mouse cursor during a drag.
2483  * @extends Roo.dd.DragDrop
2484  * @constructor
2485  * @param {String} id the id of the linked element
2486  * @param {String} sGroup the group of related DragDrop items
2487  * @param {object} config an object containing configurable attributes
2488  *                Valid properties for DD:
2489  *                    scroll
2490  */
2491 Roo.dd.DD = function(id, sGroup, config) {
2492     if (id) {
2493         this.init(id, sGroup, config);
2494     }
2495 };
2496
2497 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2498
2499     /**
2500      * When set to true, the utility automatically tries to scroll the browser
2501      * window wehn a drag and drop element is dragged near the viewport boundary.
2502      * Defaults to true.
2503      * @property scroll
2504      * @type boolean
2505      */
2506     scroll: true,
2507
2508     /**
2509      * Sets the pointer offset to the distance between the linked element's top
2510      * left corner and the location the element was clicked
2511      * @method autoOffset
2512      * @param {int} iPageX the X coordinate of the click
2513      * @param {int} iPageY the Y coordinate of the click
2514      */
2515     autoOffset: function(iPageX, iPageY) {
2516         var x = iPageX - this.startPageX;
2517         var y = iPageY - this.startPageY;
2518         this.setDelta(x, y);
2519     },
2520
2521     /**
2522      * Sets the pointer offset.  You can call this directly to force the
2523      * offset to be in a particular location (e.g., pass in 0,0 to set it
2524      * to the center of the object)
2525      * @method setDelta
2526      * @param {int} iDeltaX the distance from the left
2527      * @param {int} iDeltaY the distance from the top
2528      */
2529     setDelta: function(iDeltaX, iDeltaY) {
2530         this.deltaX = iDeltaX;
2531         this.deltaY = iDeltaY;
2532     },
2533
2534     /**
2535      * Sets the drag element to the location of the mousedown or click event,
2536      * maintaining the cursor location relative to the location on the element
2537      * that was clicked.  Override this if you want to place the element in a
2538      * location other than where the cursor is.
2539      * @method setDragElPos
2540      * @param {int} iPageX the X coordinate of the mousedown or drag event
2541      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2542      */
2543     setDragElPos: function(iPageX, iPageY) {
2544         // the first time we do this, we are going to check to make sure
2545         // the element has css positioning
2546
2547         var el = this.getDragEl();
2548         this.alignElWithMouse(el, iPageX, iPageY);
2549     },
2550
2551     /**
2552      * Sets the element to the location of the mousedown or click event,
2553      * maintaining the cursor location relative to the location on the element
2554      * that was clicked.  Override this if you want to place the element in a
2555      * location other than where the cursor is.
2556      * @method alignElWithMouse
2557      * @param {HTMLElement} el the element to move
2558      * @param {int} iPageX the X coordinate of the mousedown or drag event
2559      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2560      */
2561     alignElWithMouse: function(el, iPageX, iPageY) {
2562         var oCoord = this.getTargetCoord(iPageX, iPageY);
2563         var fly = el.dom ? el : Roo.fly(el);
2564         if (!this.deltaSetXY) {
2565             var aCoord = [oCoord.x, oCoord.y];
2566             fly.setXY(aCoord);
2567             var newLeft = fly.getLeft(true);
2568             var newTop  = fly.getTop(true);
2569             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2570         } else {
2571             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2572         }
2573
2574         this.cachePosition(oCoord.x, oCoord.y);
2575         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2576         return oCoord;
2577     },
2578
2579     /**
2580      * Saves the most recent position so that we can reset the constraints and
2581      * tick marks on-demand.  We need to know this so that we can calculate the
2582      * number of pixels the element is offset from its original position.
2583      * @method cachePosition
2584      * @param iPageX the current x position (optional, this just makes it so we
2585      * don't have to look it up again)
2586      * @param iPageY the current y position (optional, this just makes it so we
2587      * don't have to look it up again)
2588      */
2589     cachePosition: function(iPageX, iPageY) {
2590         if (iPageX) {
2591             this.lastPageX = iPageX;
2592             this.lastPageY = iPageY;
2593         } else {
2594             var aCoord = Roo.lib.Dom.getXY(this.getEl());
2595             this.lastPageX = aCoord[0];
2596             this.lastPageY = aCoord[1];
2597         }
2598     },
2599
2600     /**
2601      * Auto-scroll the window if the dragged object has been moved beyond the
2602      * visible window boundary.
2603      * @method autoScroll
2604      * @param {int} x the drag element's x position
2605      * @param {int} y the drag element's y position
2606      * @param {int} h the height of the drag element
2607      * @param {int} w the width of the drag element
2608      * @private
2609      */
2610     autoScroll: function(x, y, h, w) {
2611
2612         if (this.scroll) {
2613             // The client height
2614             var clientH = Roo.lib.Dom.getViewWidth();
2615
2616             // The client width
2617             var clientW = Roo.lib.Dom.getViewHeight();
2618
2619             // The amt scrolled down
2620             var st = this.DDM.getScrollTop();
2621
2622             // The amt scrolled right
2623             var sl = this.DDM.getScrollLeft();
2624
2625             // Location of the bottom of the element
2626             var bot = h + y;
2627
2628             // Location of the right of the element
2629             var right = w + x;
2630
2631             // The distance from the cursor to the bottom of the visible area,
2632             // adjusted so that we don't scroll if the cursor is beyond the
2633             // element drag constraints
2634             var toBot = (clientH + st - y - this.deltaY);
2635
2636             // The distance from the cursor to the right of the visible area
2637             var toRight = (clientW + sl - x - this.deltaX);
2638
2639
2640             // How close to the edge the cursor must be before we scroll
2641             // var thresh = (document.all) ? 100 : 40;
2642             var thresh = 40;
2643
2644             // How many pixels to scroll per autoscroll op.  This helps to reduce
2645             // clunky scrolling. IE is more sensitive about this ... it needs this
2646             // value to be higher.
2647             var scrAmt = (document.all) ? 80 : 30;
2648
2649             // Scroll down if we are near the bottom of the visible page and the
2650             // obj extends below the crease
2651             if ( bot > clientH && toBot < thresh ) {
2652                 window.scrollTo(sl, st + scrAmt);
2653             }
2654
2655             // Scroll up if the window is scrolled down and the top of the object
2656             // goes above the top border
2657             if ( y < st && st > 0 && y - st < thresh ) {
2658                 window.scrollTo(sl, st - scrAmt);
2659             }
2660
2661             // Scroll right if the obj is beyond the right border and the cursor is
2662             // near the border.
2663             if ( right > clientW && toRight < thresh ) {
2664                 window.scrollTo(sl + scrAmt, st);
2665             }
2666
2667             // Scroll left if the window has been scrolled to the right and the obj
2668             // extends past the left border
2669             if ( x < sl && sl > 0 && x - sl < thresh ) {
2670                 window.scrollTo(sl - scrAmt, st);
2671             }
2672         }
2673     },
2674
2675     /**
2676      * Finds the location the element should be placed if we want to move
2677      * it to where the mouse location less the click offset would place us.
2678      * @method getTargetCoord
2679      * @param {int} iPageX the X coordinate of the click
2680      * @param {int} iPageY the Y coordinate of the click
2681      * @return an object that contains the coordinates (Object.x and Object.y)
2682      * @private
2683      */
2684     getTargetCoord: function(iPageX, iPageY) {
2685
2686
2687         var x = iPageX - this.deltaX;
2688         var y = iPageY - this.deltaY;
2689
2690         if (this.constrainX) {
2691             if (x < this.minX) { x = this.minX; }
2692             if (x > this.maxX) { x = this.maxX; }
2693         }
2694
2695         if (this.constrainY) {
2696             if (y < this.minY) { y = this.minY; }
2697             if (y > this.maxY) { y = this.maxY; }
2698         }
2699
2700         x = this.getTick(x, this.xTicks);
2701         y = this.getTick(y, this.yTicks);
2702
2703
2704         return {x:x, y:y};
2705     },
2706
2707     /*
2708      * Sets up config options specific to this class. Overrides
2709      * Roo.dd.DragDrop, but all versions of this method through the
2710      * inheritance chain are called
2711      */
2712     applyConfig: function() {
2713         Roo.dd.DD.superclass.applyConfig.call(this);
2714         this.scroll = (this.config.scroll !== false);
2715     },
2716
2717     /*
2718      * Event that fires prior to the onMouseDown event.  Overrides
2719      * Roo.dd.DragDrop.
2720      */
2721     b4MouseDown: function(e) {
2722         // this.resetConstraints();
2723         this.autoOffset(e.getPageX(),
2724                             e.getPageY());
2725     },
2726
2727     /*
2728      * Event that fires prior to the onDrag event.  Overrides
2729      * Roo.dd.DragDrop.
2730      */
2731     b4Drag: function(e) {
2732         this.setDragElPos(e.getPageX(),
2733                             e.getPageY());
2734     },
2735
2736     toString: function() {
2737         return ("DD " + this.id);
2738     }
2739
2740     //////////////////////////////////////////////////////////////////////////
2741     // Debugging ygDragDrop events that can be overridden
2742     //////////////////////////////////////////////////////////////////////////
2743     /*
2744     startDrag: function(x, y) {
2745     },
2746
2747     onDrag: function(e) {
2748     },
2749
2750     onDragEnter: function(e, id) {
2751     },
2752
2753     onDragOver: function(e, id) {
2754     },
2755
2756     onDragOut: function(e, id) {
2757     },
2758
2759     onDragDrop: function(e, id) {
2760     },
2761
2762     endDrag: function(e) {
2763     }
2764
2765     */
2766
2767 });/*
2768  * Based on:
2769  * Ext JS Library 1.1.1
2770  * Copyright(c) 2006-2007, Ext JS, LLC.
2771  *
2772  * Originally Released Under LGPL - original licence link has changed is not relivant.
2773  *
2774  * Fork - LGPL
2775  * <script type="text/javascript">
2776  */
2777
2778 /**
2779  * @class Roo.dd.DDProxy
2780  * A DragDrop implementation that inserts an empty, bordered div into
2781  * the document that follows the cursor during drag operations.  At the time of
2782  * the click, the frame div is resized to the dimensions of the linked html
2783  * element, and moved to the exact location of the linked element.
2784  *
2785  * References to the "frame" element refer to the single proxy element that
2786  * was created to be dragged in place of all DDProxy elements on the
2787  * page.
2788  *
2789  * @extends Roo.dd.DD
2790  * @constructor
2791  * @param {String} id the id of the linked html element
2792  * @param {String} sGroup the group of related DragDrop objects
2793  * @param {object} config an object containing configurable attributes
2794  *                Valid properties for DDProxy in addition to those in DragDrop:
2795  *                   resizeFrame, centerFrame, dragElId
2796  */
2797 Roo.dd.DDProxy = function(id, sGroup, config) {
2798     if (id) {
2799         this.init(id, sGroup, config);
2800         this.initFrame();
2801     }
2802 };
2803
2804 /**
2805  * The default drag frame div id
2806  * @property Roo.dd.DDProxy.dragElId
2807  * @type String
2808  * @static
2809  */
2810 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2811
2812 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2813
2814     /**
2815      * By default we resize the drag frame to be the same size as the element
2816      * we want to drag (this is to get the frame effect).  We can turn it off
2817      * if we want a different behavior.
2818      * @property resizeFrame
2819      * @type boolean
2820      */
2821     resizeFrame: true,
2822
2823     /**
2824      * By default the frame is positioned exactly where the drag element is, so
2825      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
2826      * you do not have constraints on the obj is to have the drag frame centered
2827      * around the cursor.  Set centerFrame to true for this effect.
2828      * @property centerFrame
2829      * @type boolean
2830      */
2831     centerFrame: false,
2832
2833     /**
2834      * Creates the proxy element if it does not yet exist
2835      * @method createFrame
2836      */
2837     createFrame: function() {
2838         var self = this;
2839         var body = document.body;
2840
2841         if (!body || !body.firstChild) {
2842             setTimeout( function() { self.createFrame(); }, 50 );
2843             return;
2844         }
2845
2846         var div = this.getDragEl();
2847
2848         if (!div) {
2849             div    = document.createElement("div");
2850             div.id = this.dragElId;
2851             var s  = div.style;
2852
2853             s.position   = "absolute";
2854             s.visibility = "hidden";
2855             s.cursor     = "move";
2856             s.border     = "2px solid #aaa";
2857             s.zIndex     = 999;
2858
2859             // appendChild can blow up IE if invoked prior to the window load event
2860             // while rendering a table.  It is possible there are other scenarios
2861             // that would cause this to happen as well.
2862             body.insertBefore(div, body.firstChild);
2863         }
2864     },
2865
2866     /**
2867      * Initialization for the drag frame element.  Must be called in the
2868      * constructor of all subclasses
2869      * @method initFrame
2870      */
2871     initFrame: function() {
2872         this.createFrame();
2873     },
2874
2875     applyConfig: function() {
2876         Roo.dd.DDProxy.superclass.applyConfig.call(this);
2877
2878         this.resizeFrame = (this.config.resizeFrame !== false);
2879         this.centerFrame = (this.config.centerFrame);
2880         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2881     },
2882
2883     /**
2884      * Resizes the drag frame to the dimensions of the clicked object, positions
2885      * it over the object, and finally displays it
2886      * @method showFrame
2887      * @param {int} iPageX X click position
2888      * @param {int} iPageY Y click position
2889      * @private
2890      */
2891     showFrame: function(iPageX, iPageY) {
2892         var el = this.getEl();
2893         var dragEl = this.getDragEl();
2894         var s = dragEl.style;
2895
2896         this._resizeProxy();
2897
2898         if (this.centerFrame) {
2899             this.setDelta( Math.round(parseInt(s.width,  10)/2),
2900                            Math.round(parseInt(s.height, 10)/2) );
2901         }
2902
2903         this.setDragElPos(iPageX, iPageY);
2904
2905         Roo.fly(dragEl).show();
2906     },
2907
2908     /**
2909      * The proxy is automatically resized to the dimensions of the linked
2910      * element when a drag is initiated, unless resizeFrame is set to false
2911      * @method _resizeProxy
2912      * @private
2913      */
2914     _resizeProxy: function() {
2915         if (this.resizeFrame) {
2916             var el = this.getEl();
2917             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2918         }
2919     },
2920
2921     // overrides Roo.dd.DragDrop
2922     b4MouseDown: function(e) {
2923         var x = e.getPageX();
2924         var y = e.getPageY();
2925         this.autoOffset(x, y);
2926         this.setDragElPos(x, y);
2927     },
2928
2929     // overrides Roo.dd.DragDrop
2930     b4StartDrag: function(x, y) {
2931         // show the drag frame
2932         this.showFrame(x, y);
2933     },
2934
2935     // overrides Roo.dd.DragDrop
2936     b4EndDrag: function(e) {
2937         Roo.fly(this.getDragEl()).hide();
2938     },
2939
2940     // overrides Roo.dd.DragDrop
2941     // By default we try to move the element to the last location of the frame.
2942     // This is so that the default behavior mirrors that of Roo.dd.DD.
2943     endDrag: function(e) {
2944
2945         var lel = this.getEl();
2946         var del = this.getDragEl();
2947
2948         // Show the drag frame briefly so we can get its position
2949         del.style.visibility = "";
2950
2951         this.beforeMove();
2952         // Hide the linked element before the move to get around a Safari
2953         // rendering bug.
2954         lel.style.visibility = "hidden";
2955         Roo.dd.DDM.moveToEl(lel, del);
2956         del.style.visibility = "hidden";
2957         lel.style.visibility = "";
2958
2959         this.afterDrag();
2960     },
2961
2962     beforeMove : function(){
2963
2964     },
2965
2966     afterDrag : function(){
2967
2968     },
2969
2970     toString: function() {
2971         return ("DDProxy " + this.id);
2972     }
2973
2974 });
2975 /*
2976  * Based on:
2977  * Ext JS Library 1.1.1
2978  * Copyright(c) 2006-2007, Ext JS, LLC.
2979  *
2980  * Originally Released Under LGPL - original licence link has changed is not relivant.
2981  *
2982  * Fork - LGPL
2983  * <script type="text/javascript">
2984  */
2985
2986  /**
2987  * @class Roo.dd.DDTarget
2988  * A DragDrop implementation that does not move, but can be a drop
2989  * target.  You would get the same result by simply omitting implementation
2990  * for the event callbacks, but this way we reduce the processing cost of the
2991  * event listener and the callbacks.
2992  * @extends Roo.dd.DragDrop
2993  * @constructor
2994  * @param {String} id the id of the element that is a drop target
2995  * @param {String} sGroup the group of related DragDrop objects
2996  * @param {object} config an object containing configurable attributes
2997  *                 Valid properties for DDTarget in addition to those in
2998  *                 DragDrop:
2999  *                    none
3000  */
3001 Roo.dd.DDTarget = function(id, sGroup, config) {
3002     if (id) {
3003         this.initTarget(id, sGroup, config);
3004     }
3005     if (config.listeners || config.events) { 
3006        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
3007             listeners : config.listeners || {}, 
3008             events : config.events || {} 
3009         });    
3010     }
3011 };
3012
3013 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3014 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3015     toString: function() {
3016         return ("DDTarget " + this.id);
3017     }
3018 });
3019 /*
3020  * Based on:
3021  * Ext JS Library 1.1.1
3022  * Copyright(c) 2006-2007, Ext JS, LLC.
3023  *
3024  * Originally Released Under LGPL - original licence link has changed is not relivant.
3025  *
3026  * Fork - LGPL
3027  * <script type="text/javascript">
3028  */
3029  
3030
3031 /**
3032  * @class Roo.dd.ScrollManager
3033  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3034  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3035  * @singleton
3036  */
3037 Roo.dd.ScrollManager = function(){
3038     var ddm = Roo.dd.DragDropMgr;
3039     var els = {};
3040     var dragEl = null;
3041     var proc = {};
3042     
3043     
3044     
3045     var onStop = function(e){
3046         dragEl = null;
3047         clearProc();
3048     };
3049     
3050     var triggerRefresh = function(){
3051         if(ddm.dragCurrent){
3052              ddm.refreshCache(ddm.dragCurrent.groups);
3053         }
3054     };
3055     
3056     var doScroll = function(){
3057         if(ddm.dragCurrent){
3058             var dds = Roo.dd.ScrollManager;
3059             if(!dds.animate){
3060                 if(proc.el.scroll(proc.dir, dds.increment)){
3061                     triggerRefresh();
3062                 }
3063             }else{
3064                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3065             }
3066         }
3067     };
3068     
3069     var clearProc = function(){
3070         if(proc.id){
3071             clearInterval(proc.id);
3072         }
3073         proc.id = 0;
3074         proc.el = null;
3075         proc.dir = "";
3076     };
3077     
3078     var startProc = function(el, dir){
3079          Roo.log('scroll startproc');
3080         clearProc();
3081         proc.el = el;
3082         proc.dir = dir;
3083         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3084     };
3085     
3086     var onFire = function(e, isDrop){
3087        
3088         if(isDrop || !ddm.dragCurrent){ return; }
3089         var dds = Roo.dd.ScrollManager;
3090         if(!dragEl || dragEl != ddm.dragCurrent){
3091             dragEl = ddm.dragCurrent;
3092             // refresh regions on drag start
3093             dds.refreshCache();
3094         }
3095         
3096         var xy = Roo.lib.Event.getXY(e);
3097         var pt = new Roo.lib.Point(xy[0], xy[1]);
3098         for(var id in els){
3099             var el = els[id], r = el._region;
3100             if(r && r.contains(pt) && el.isScrollable()){
3101                 if(r.bottom - pt.y <= dds.thresh){
3102                     if(proc.el != el){
3103                         startProc(el, "down");
3104                     }
3105                     return;
3106                 }else if(r.right - pt.x <= dds.thresh){
3107                     if(proc.el != el){
3108                         startProc(el, "left");
3109                     }
3110                     return;
3111                 }else if(pt.y - r.top <= dds.thresh){
3112                     if(proc.el != el){
3113                         startProc(el, "up");
3114                     }
3115                     return;
3116                 }else if(pt.x - r.left <= dds.thresh){
3117                     if(proc.el != el){
3118                         startProc(el, "right");
3119                     }
3120                     return;
3121                 }
3122             }
3123         }
3124         clearProc();
3125     };
3126     
3127     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3128     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3129     
3130     return {
3131         /**
3132          * Registers new overflow element(s) to auto scroll
3133          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3134          */
3135         register : function(el){
3136             if(el instanceof Array){
3137                 for(var i = 0, len = el.length; i < len; i++) {
3138                         this.register(el[i]);
3139                 }
3140             }else{
3141                 el = Roo.get(el);
3142                 els[el.id] = el;
3143             }
3144             Roo.dd.ScrollManager.els = els;
3145         },
3146         
3147         /**
3148          * Unregisters overflow element(s) so they are no longer scrolled
3149          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3150          */
3151         unregister : function(el){
3152             if(el instanceof Array){
3153                 for(var i = 0, len = el.length; i < len; i++) {
3154                         this.unregister(el[i]);
3155                 }
3156             }else{
3157                 el = Roo.get(el);
3158                 delete els[el.id];
3159             }
3160         },
3161         
3162         /**
3163          * The number of pixels from the edge of a container the pointer needs to be to 
3164          * trigger scrolling (defaults to 25)
3165          * @type Number
3166          */
3167         thresh : 25,
3168         
3169         /**
3170          * The number of pixels to scroll in each scroll increment (defaults to 50)
3171          * @type Number
3172          */
3173         increment : 100,
3174         
3175         /**
3176          * The frequency of scrolls in milliseconds (defaults to 500)
3177          * @type Number
3178          */
3179         frequency : 500,
3180         
3181         /**
3182          * True to animate the scroll (defaults to true)
3183          * @type Boolean
3184          */
3185         animate: true,
3186         
3187         /**
3188          * The animation duration in seconds - 
3189          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3190          * @type Number
3191          */
3192         animDuration: .4,
3193         
3194         /**
3195          * Manually trigger a cache refresh.
3196          */
3197         refreshCache : function(){
3198             for(var id in els){
3199                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3200                     els[id]._region = els[id].getRegion();
3201                 }
3202             }
3203         }
3204     };
3205 }();/*
3206  * Based on:
3207  * Ext JS Library 1.1.1
3208  * Copyright(c) 2006-2007, Ext JS, LLC.
3209  *
3210  * Originally Released Under LGPL - original licence link has changed is not relivant.
3211  *
3212  * Fork - LGPL
3213  * <script type="text/javascript">
3214  */
3215  
3216
3217 /**
3218  * @class Roo.dd.Registry
3219  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3220  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3221  * @singleton
3222  */
3223 Roo.dd.Registry = function(){
3224     var elements = {}; 
3225     var handles = {}; 
3226     var autoIdSeed = 0;
3227
3228     var getId = function(el, autogen){
3229         if(typeof el == "string"){
3230             return el;
3231         }
3232         var id = el.id;
3233         if(!id && autogen !== false){
3234             id = "roodd-" + (++autoIdSeed);
3235             el.id = id;
3236         }
3237         return id;
3238     };
3239     
3240     return {
3241     /**
3242      * Register a drag drop element
3243      * @param {String|HTMLElement} element The id or DOM node to register
3244      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3245      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3246      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3247      * populated in the data object (if applicable):
3248      * <pre>
3249 Value      Description<br />
3250 ---------  ------------------------------------------<br />
3251 handles    Array of DOM nodes that trigger dragging<br />
3252            for the element being registered<br />
3253 isHandle   True if the element passed in triggers<br />
3254            dragging itself, else false
3255 </pre>
3256      */
3257         register : function(el, data){
3258             data = data || {};
3259             if(typeof el == "string"){
3260                 el = document.getElementById(el);
3261             }
3262             data.ddel = el;
3263             elements[getId(el)] = data;
3264             if(data.isHandle !== false){
3265                 handles[data.ddel.id] = data;
3266             }
3267             if(data.handles){
3268                 var hs = data.handles;
3269                 for(var i = 0, len = hs.length; i < len; i++){
3270                         handles[getId(hs[i])] = data;
3271                 }
3272             }
3273         },
3274
3275     /**
3276      * Unregister a drag drop element
3277      * @param {String|HTMLElement}  element The id or DOM node to unregister
3278      */
3279         unregister : function(el){
3280             var id = getId(el, false);
3281             var data = elements[id];
3282             if(data){
3283                 delete elements[id];
3284                 if(data.handles){
3285                     var hs = data.handles;
3286                     for(var i = 0, len = hs.length; i < len; i++){
3287                         delete handles[getId(hs[i], false)];
3288                     }
3289                 }
3290             }
3291         },
3292
3293     /**
3294      * Returns the handle registered for a DOM Node by id
3295      * @param {String|HTMLElement} id The DOM node or id to look up
3296      * @return {Object} handle The custom handle data
3297      */
3298         getHandle : function(id){
3299             if(typeof id != "string"){ // must be element?
3300                 id = id.id;
3301             }
3302             return handles[id];
3303         },
3304
3305     /**
3306      * Returns the handle that is registered for the DOM node that is the target of the event
3307      * @param {Event} e The event
3308      * @return {Object} handle The custom handle data
3309      */
3310         getHandleFromEvent : function(e){
3311             var t = Roo.lib.Event.getTarget(e);
3312             return t ? handles[t.id] : null;
3313         },
3314
3315     /**
3316      * Returns a custom data object that is registered for a DOM node by id
3317      * @param {String|HTMLElement} id The DOM node or id to look up
3318      * @return {Object} data The custom data
3319      */
3320         getTarget : function(id){
3321             if(typeof id != "string"){ // must be element?
3322                 id = id.id;
3323             }
3324             return elements[id];
3325         },
3326
3327     /**
3328      * Returns a custom data object that is registered for the DOM node that is the target of the event
3329      * @param {Event} e The event
3330      * @return {Object} data The custom data
3331      */
3332         getTargetFromEvent : function(e){
3333             var t = Roo.lib.Event.getTarget(e);
3334             return t ? elements[t.id] || handles[t.id] : null;
3335         }
3336     };
3337 }();/*
3338  * Based on:
3339  * Ext JS Library 1.1.1
3340  * Copyright(c) 2006-2007, Ext JS, LLC.
3341  *
3342  * Originally Released Under LGPL - original licence link has changed is not relivant.
3343  *
3344  * Fork - LGPL
3345  * <script type="text/javascript">
3346  */
3347  
3348
3349 /**
3350  * @class Roo.dd.StatusProxy
3351  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3352  * default drag proxy used by all Roo.dd components.
3353  * @constructor
3354  * @param {Object} config
3355  */
3356 Roo.dd.StatusProxy = function(config){
3357     Roo.apply(this, config);
3358     this.id = this.id || Roo.id();
3359     this.el = new Roo.Layer({
3360         dh: {
3361             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3362                 {tag: "div", cls: "x-dd-drop-icon"},
3363                 {tag: "div", cls: "x-dd-drag-ghost"}
3364             ]
3365         }, 
3366         shadow: !config || config.shadow !== false
3367     });
3368     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3369     this.dropStatus = this.dropNotAllowed;
3370 };
3371
3372 Roo.dd.StatusProxy.prototype = {
3373     /**
3374      * @cfg {String} dropAllowed
3375      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3376      */
3377     dropAllowed : "x-dd-drop-ok",
3378     /**
3379      * @cfg {String} dropNotAllowed
3380      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3381      */
3382     dropNotAllowed : "x-dd-drop-nodrop",
3383
3384     /**
3385      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3386      * over the current target element.
3387      * @param {String} cssClass The css class for the new drop status indicator image
3388      */
3389     setStatus : function(cssClass){
3390         cssClass = cssClass || this.dropNotAllowed;
3391         if(this.dropStatus != cssClass){
3392             this.el.replaceClass(this.dropStatus, cssClass);
3393             this.dropStatus = cssClass;
3394         }
3395     },
3396
3397     /**
3398      * Resets the status indicator to the default dropNotAllowed value
3399      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3400      */
3401     reset : function(clearGhost){
3402         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3403         this.dropStatus = this.dropNotAllowed;
3404         if(clearGhost){
3405             this.ghost.update("");
3406         }
3407     },
3408
3409     /**
3410      * Updates the contents of the ghost element
3411      * @param {String} html The html that will replace the current innerHTML of the ghost element
3412      */
3413     update : function(html){
3414         if(typeof html == "string"){
3415             this.ghost.update(html);
3416         }else{
3417             this.ghost.update("");
3418             html.style.margin = "0";
3419             this.ghost.dom.appendChild(html);
3420         }
3421         // ensure float = none set?? cant remember why though.
3422         var el = this.ghost.dom.firstChild;
3423                 if(el){
3424                         Roo.fly(el).setStyle('float', 'none');
3425                 }
3426     },
3427     
3428     /**
3429      * Returns the underlying proxy {@link Roo.Layer}
3430      * @return {Roo.Layer} el
3431     */
3432     getEl : function(){
3433         return this.el;
3434     },
3435
3436     /**
3437      * Returns the ghost element
3438      * @return {Roo.Element} el
3439      */
3440     getGhost : function(){
3441         return this.ghost;
3442     },
3443
3444     /**
3445      * Hides the proxy
3446      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3447      */
3448     hide : function(clear){
3449         this.el.hide();
3450         if(clear){
3451             this.reset(true);
3452         }
3453     },
3454
3455     /**
3456      * Stops the repair animation if it's currently running
3457      */
3458     stop : function(){
3459         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3460             this.anim.stop();
3461         }
3462     },
3463
3464     /**
3465      * Displays this proxy
3466      */
3467     show : function(){
3468         this.el.show();
3469     },
3470
3471     /**
3472      * Force the Layer to sync its shadow and shim positions to the element
3473      */
3474     sync : function(){
3475         this.el.sync();
3476     },
3477
3478     /**
3479      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3480      * invalid drop operation by the item being dragged.
3481      * @param {Array} xy The XY position of the element ([x, y])
3482      * @param {Function} callback The function to call after the repair is complete
3483      * @param {Object} scope The scope in which to execute the callback
3484      */
3485     repair : function(xy, callback, scope){
3486         this.callback = callback;
3487         this.scope = scope;
3488         if(xy && this.animRepair !== false){
3489             this.el.addClass("x-dd-drag-repair");
3490             this.el.hideUnders(true);
3491             this.anim = this.el.shift({
3492                 duration: this.repairDuration || .5,
3493                 easing: 'easeOut',
3494                 xy: xy,
3495                 stopFx: true,
3496                 callback: this.afterRepair,
3497                 scope: this
3498             });
3499         }else{
3500             this.afterRepair();
3501         }
3502     },
3503
3504     // private
3505     afterRepair : function(){
3506         this.hide(true);
3507         if(typeof this.callback == "function"){
3508             this.callback.call(this.scope || this);
3509         }
3510         this.callback = null;
3511         this.scope = null;
3512     }
3513 };/*
3514  * Based on:
3515  * Ext JS Library 1.1.1
3516  * Copyright(c) 2006-2007, Ext JS, LLC.
3517  *
3518  * Originally Released Under LGPL - original licence link has changed is not relivant.
3519  *
3520  * Fork - LGPL
3521  * <script type="text/javascript">
3522  */
3523
3524 /**
3525  * @class Roo.dd.DragSource
3526  * @extends Roo.dd.DDProxy
3527  * A simple class that provides the basic implementation needed to make any element draggable.
3528  * @constructor
3529  * @param {String/HTMLElement/Element} el The container element
3530  * @param {Object} config
3531  */
3532 Roo.dd.DragSource = function(el, config){
3533     this.el = Roo.get(el);
3534     this.dragData = {};
3535     
3536     Roo.apply(this, config);
3537     
3538     if(!this.proxy){
3539         this.proxy = new Roo.dd.StatusProxy();
3540     }
3541
3542     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3543           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3544     
3545     this.dragging = false;
3546 };
3547
3548 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3549     /**
3550      * @cfg {String} dropAllowed
3551      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3552      */
3553     dropAllowed : "x-dd-drop-ok",
3554     /**
3555      * @cfg {String} dropNotAllowed
3556      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3557      */
3558     dropNotAllowed : "x-dd-drop-nodrop",
3559
3560     /**
3561      * Returns the data object associated with this drag source
3562      * @return {Object} data An object containing arbitrary data
3563      */
3564     getDragData : function(e){
3565         return this.dragData;
3566     },
3567
3568     // private
3569     onDragEnter : function(e, id){
3570         var target = Roo.dd.DragDropMgr.getDDById(id);
3571         this.cachedTarget = target;
3572         if(this.beforeDragEnter(target, e, id) !== false){
3573             if(target.isNotifyTarget){
3574                 var status = target.notifyEnter(this, e, this.dragData);
3575                 this.proxy.setStatus(status);
3576             }else{
3577                 this.proxy.setStatus(this.dropAllowed);
3578             }
3579             
3580             if(this.afterDragEnter){
3581                 /**
3582                  * An empty function by default, but provided so that you can perform a custom action
3583                  * when the dragged item enters the drop target by providing an implementation.
3584                  * @param {Roo.dd.DragDrop} target The drop target
3585                  * @param {Event} e The event object
3586                  * @param {String} id The id of the dragged element
3587                  * @method afterDragEnter
3588                  */
3589                 this.afterDragEnter(target, e, id);
3590             }
3591         }
3592     },
3593
3594     /**
3595      * An empty function by default, but provided so that you can perform a custom action
3596      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3597      * @param {Roo.dd.DragDrop} target The drop target
3598      * @param {Event} e The event object
3599      * @param {String} id The id of the dragged element
3600      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3601      */
3602     beforeDragEnter : function(target, e, id){
3603         return true;
3604     },
3605
3606     // private
3607     alignElWithMouse: function() {
3608         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3609         this.proxy.sync();
3610     },
3611
3612     // private
3613     onDragOver : function(e, id){
3614         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3615         if(this.beforeDragOver(target, e, id) !== false){
3616             if(target.isNotifyTarget){
3617                 var status = target.notifyOver(this, e, this.dragData);
3618                 this.proxy.setStatus(status);
3619             }
3620
3621             if(this.afterDragOver){
3622                 /**
3623                  * An empty function by default, but provided so that you can perform a custom action
3624                  * while the dragged item is over the drop target by providing an implementation.
3625                  * @param {Roo.dd.DragDrop} target The drop target
3626                  * @param {Event} e The event object
3627                  * @param {String} id The id of the dragged element
3628                  * @method afterDragOver
3629                  */
3630                 this.afterDragOver(target, e, id);
3631             }
3632         }
3633     },
3634
3635     /**
3636      * An empty function by default, but provided so that you can perform a custom action
3637      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3638      * @param {Roo.dd.DragDrop} target The drop target
3639      * @param {Event} e The event object
3640      * @param {String} id The id of the dragged element
3641      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3642      */
3643     beforeDragOver : function(target, e, id){
3644         return true;
3645     },
3646
3647     // private
3648     onDragOut : function(e, id){
3649         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3650         if(this.beforeDragOut(target, e, id) !== false){
3651             if(target.isNotifyTarget){
3652                 target.notifyOut(this, e, this.dragData);
3653             }
3654             this.proxy.reset();
3655             if(this.afterDragOut){
3656                 /**
3657                  * An empty function by default, but provided so that you can perform a custom action
3658                  * after the dragged item is dragged out of the target without dropping.
3659                  * @param {Roo.dd.DragDrop} target The drop target
3660                  * @param {Event} e The event object
3661                  * @param {String} id The id of the dragged element
3662                  * @method afterDragOut
3663                  */
3664                 this.afterDragOut(target, e, id);
3665             }
3666         }
3667         this.cachedTarget = null;
3668     },
3669
3670     /**
3671      * An empty function by default, but provided so that you can perform a custom action before the dragged
3672      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3673      * @param {Roo.dd.DragDrop} target The drop target
3674      * @param {Event} e The event object
3675      * @param {String} id The id of the dragged element
3676      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3677      */
3678     beforeDragOut : function(target, e, id){
3679         return true;
3680     },
3681     
3682     // private
3683     onDragDrop : function(e, id){
3684         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3685         if(this.beforeDragDrop(target, e, id) !== false){
3686             if(target.isNotifyTarget){
3687                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3688                     this.onValidDrop(target, e, id);
3689                 }else{
3690                     this.onInvalidDrop(target, e, id);
3691                 }
3692             }else{
3693                 this.onValidDrop(target, e, id);
3694             }
3695             
3696             if(this.afterDragDrop){
3697                 /**
3698                  * An empty function by default, but provided so that you can perform a custom action
3699                  * after a valid drag drop has occurred by providing an implementation.
3700                  * @param {Roo.dd.DragDrop} target The drop target
3701                  * @param {Event} e The event object
3702                  * @param {String} id The id of the dropped element
3703                  * @method afterDragDrop
3704                  */
3705                 this.afterDragDrop(target, e, id);
3706             }
3707         }
3708         delete this.cachedTarget;
3709     },
3710
3711     /**
3712      * An empty function by default, but provided so that you can perform a custom action before the dragged
3713      * item is dropped onto the target and optionally cancel the onDragDrop.
3714      * @param {Roo.dd.DragDrop} target The drop target
3715      * @param {Event} e The event object
3716      * @param {String} id The id of the dragged element
3717      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3718      */
3719     beforeDragDrop : function(target, e, id){
3720         return true;
3721     },
3722
3723     // private
3724     onValidDrop : function(target, e, id){
3725         this.hideProxy();
3726         if(this.afterValidDrop){
3727             /**
3728              * An empty function by default, but provided so that you can perform a custom action
3729              * after a valid drop has occurred by providing an implementation.
3730              * @param {Object} target The target DD 
3731              * @param {Event} e The event object
3732              * @param {String} id The id of the dropped element
3733              * @method afterInvalidDrop
3734              */
3735             this.afterValidDrop(target, e, id);
3736         }
3737     },
3738
3739     // private
3740     getRepairXY : function(e, data){
3741         return this.el.getXY();  
3742     },
3743
3744     // private
3745     onInvalidDrop : function(target, e, id){
3746         this.beforeInvalidDrop(target, e, id);
3747         if(this.cachedTarget){
3748             if(this.cachedTarget.isNotifyTarget){
3749                 this.cachedTarget.notifyOut(this, e, this.dragData);
3750             }
3751             this.cacheTarget = null;
3752         }
3753         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3754
3755         if(this.afterInvalidDrop){
3756             /**
3757              * An empty function by default, but provided so that you can perform a custom action
3758              * after an invalid drop has occurred by providing an implementation.
3759              * @param {Event} e The event object
3760              * @param {String} id The id of the dropped element
3761              * @method afterInvalidDrop
3762              */
3763             this.afterInvalidDrop(e, id);
3764         }
3765     },
3766
3767     // private
3768     afterRepair : function(){
3769         if(Roo.enableFx){
3770             this.el.highlight(this.hlColor || "c3daf9");
3771         }
3772         this.dragging = false;
3773     },
3774
3775     /**
3776      * An empty function by default, but provided so that you can perform a custom action after an invalid
3777      * drop has occurred.
3778      * @param {Roo.dd.DragDrop} target The drop target
3779      * @param {Event} e The event object
3780      * @param {String} id The id of the dragged element
3781      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3782      */
3783     beforeInvalidDrop : function(target, e, id){
3784         return true;
3785     },
3786
3787     // private
3788     handleMouseDown : function(e){
3789         if(this.dragging) {
3790             return;
3791         }
3792         var data = this.getDragData(e);
3793         if(data && this.onBeforeDrag(data, e) !== false){
3794             this.dragData = data;
3795             this.proxy.stop();
3796             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3797         } 
3798     },
3799
3800     /**
3801      * An empty function by default, but provided so that you can perform a custom action before the initial
3802      * drag event begins and optionally cancel it.
3803      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3804      * @param {Event} e The event object
3805      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3806      */
3807     onBeforeDrag : function(data, e){
3808         return true;
3809     },
3810
3811     /**
3812      * An empty function by default, but provided so that you can perform a custom action once the initial
3813      * drag event has begun.  The drag cannot be canceled from this function.
3814      * @param {Number} x The x position of the click on the dragged object
3815      * @param {Number} y The y position of the click on the dragged object
3816      */
3817     onStartDrag : Roo.emptyFn,
3818
3819     // private - YUI override
3820     startDrag : function(x, y){
3821         this.proxy.reset();
3822         this.dragging = true;
3823         this.proxy.update("");
3824         this.onInitDrag(x, y);
3825         this.proxy.show();
3826     },
3827
3828     // private
3829     onInitDrag : function(x, y){
3830         var clone = this.el.dom.cloneNode(true);
3831         clone.id = Roo.id(); // prevent duplicate ids
3832         this.proxy.update(clone);
3833         this.onStartDrag(x, y);
3834         return true;
3835     },
3836
3837     /**
3838      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3839      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3840      */
3841     getProxy : function(){
3842         return this.proxy;  
3843     },
3844
3845     /**
3846      * Hides the drag source's {@link Roo.dd.StatusProxy}
3847      */
3848     hideProxy : function(){
3849         this.proxy.hide();  
3850         this.proxy.reset(true);
3851         this.dragging = false;
3852     },
3853
3854     // private
3855     triggerCacheRefresh : function(){
3856         Roo.dd.DDM.refreshCache(this.groups);
3857     },
3858
3859     // private - override to prevent hiding
3860     b4EndDrag: function(e) {
3861     },
3862
3863     // private - override to prevent moving
3864     endDrag : function(e){
3865         this.onEndDrag(this.dragData, e);
3866     },
3867
3868     // private
3869     onEndDrag : function(data, e){
3870     },
3871     
3872     // private - pin to cursor
3873     autoOffset : function(x, y) {
3874         this.setDelta(-12, -20);
3875     }    
3876 });/*
3877  * Based on:
3878  * Ext JS Library 1.1.1
3879  * Copyright(c) 2006-2007, Ext JS, LLC.
3880  *
3881  * Originally Released Under LGPL - original licence link has changed is not relivant.
3882  *
3883  * Fork - LGPL
3884  * <script type="text/javascript">
3885  */
3886
3887
3888 /**
3889  * @class Roo.dd.DropTarget
3890  * @extends Roo.dd.DDTarget
3891  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3892  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3893  * @constructor
3894  * @param {String/HTMLElement/Element} el The container element
3895  * @param {Object} config
3896  */
3897 Roo.dd.DropTarget = function(el, config){
3898     this.el = Roo.get(el);
3899     
3900     var listeners = false; ;
3901     if (config && config.listeners) {
3902         listeners= config.listeners;
3903         delete config.listeners;
3904     }
3905     Roo.apply(this, config);
3906     
3907     if(this.containerScroll){
3908         Roo.dd.ScrollManager.register(this.el);
3909     }
3910     this.addEvents( {
3911          /**
3912          * @scope Roo.dd.DropTarget
3913          */
3914          
3915          /**
3916          * @event enter
3917          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3918          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3919          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3920          * 
3921          * IMPORTANT : it should set this.overClass and this.dropAllowed
3922          * 
3923          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3924          * @param {Event} e The event
3925          * @param {Object} data An object containing arbitrary data supplied by the drag source
3926          */
3927         "enter" : true,
3928         
3929          /**
3930          * @event over
3931          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3932          * This method will be called on every mouse movement while the drag source is over the drop target.
3933          * This default implementation simply returns the dropAllowed config value.
3934          * 
3935          * IMPORTANT : it should set this.dropAllowed
3936          * 
3937          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3938          * @param {Event} e The event
3939          * @param {Object} data An object containing arbitrary data supplied by the drag source
3940          
3941          */
3942         "over" : true,
3943         /**
3944          * @event out
3945          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3946          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3947          * overClass (if any) from the drop element.
3948          * 
3949          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3950          * @param {Event} e The event
3951          * @param {Object} data An object containing arbitrary data supplied by the drag source
3952          */
3953          "out" : true,
3954          
3955         /**
3956          * @event drop
3957          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3958          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3959          * implementation that does something to process the drop event and returns true so that the drag source's
3960          * repair action does not run.
3961          * 
3962          * IMPORTANT : it should set this.success
3963          * 
3964          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3965          * @param {Event} e The event
3966          * @param {Object} data An object containing arbitrary data supplied by the drag source
3967         */
3968          "drop" : true
3969     });
3970             
3971      
3972     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3973         this.el.dom, 
3974         this.ddGroup || this.group,
3975         {
3976             isTarget: true,
3977             listeners : listeners || {} 
3978            
3979         
3980         }
3981     );
3982
3983 };
3984
3985 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
3986     /**
3987      * @cfg {String} overClass
3988      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
3989      */
3990      /**
3991      * @cfg {String} ddGroup
3992      * The drag drop group to handle drop events for
3993      */
3994      
3995     /**
3996      * @cfg {String} dropAllowed
3997      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3998      */
3999     dropAllowed : "x-dd-drop-ok",
4000     /**
4001      * @cfg {String} dropNotAllowed
4002      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
4003      */
4004     dropNotAllowed : "x-dd-drop-nodrop",
4005     /**
4006      * @cfg {boolean} success
4007      * set this after drop listener.. 
4008      */
4009     success : false,
4010     /**
4011      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
4012      * if the drop point is valid for over/enter..
4013      */
4014     valid : false,
4015     // private
4016     isTarget : true,
4017
4018     // private
4019     isNotifyTarget : true,
4020     
4021     /**
4022      * @hide
4023      */
4024     notifyEnter : function(dd, e, data)
4025     {
4026         this.valid = true;
4027         this.fireEvent('enter', dd, e, data);
4028         if(this.overClass){
4029             this.el.addClass(this.overClass);
4030         }
4031         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4032             this.valid ? this.dropAllowed : this.dropNotAllowed
4033         );
4034     },
4035
4036     /**
4037      * @hide
4038      */
4039     notifyOver : function(dd, e, data)
4040     {
4041         this.valid = true;
4042         this.fireEvent('over', dd, e, data);
4043         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4044             this.valid ? this.dropAllowed : this.dropNotAllowed
4045         );
4046     },
4047
4048     /**
4049      * @hide
4050      */
4051     notifyOut : function(dd, e, data)
4052     {
4053         this.fireEvent('out', dd, e, data);
4054         if(this.overClass){
4055             this.el.removeClass(this.overClass);
4056         }
4057     },
4058
4059     /**
4060      * @hide
4061      */
4062     notifyDrop : function(dd, e, data)
4063     {
4064         this.success = false;
4065         this.fireEvent('drop', dd, e, data);
4066         return this.success;
4067     }
4068 });/*
4069  * Based on:
4070  * Ext JS Library 1.1.1
4071  * Copyright(c) 2006-2007, Ext JS, LLC.
4072  *
4073  * Originally Released Under LGPL - original licence link has changed is not relivant.
4074  *
4075  * Fork - LGPL
4076  * <script type="text/javascript">
4077  */
4078
4079
4080 /**
4081  * @class Roo.dd.DragZone
4082  * @extends Roo.dd.DragSource
4083  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4084  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4085  * @constructor
4086  * @param {String/HTMLElement/Element} el The container element
4087  * @param {Object} config
4088  */
4089 Roo.dd.DragZone = function(el, config){
4090     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4091     if(this.containerScroll){
4092         Roo.dd.ScrollManager.register(this.el);
4093     }
4094 };
4095
4096 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4097     /**
4098      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4099      * for auto scrolling during drag operations.
4100      */
4101     /**
4102      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4103      * method after a failed drop (defaults to "c3daf9" - light blue)
4104      */
4105
4106     /**
4107      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4108      * for a valid target to drag based on the mouse down. Override this method
4109      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4110      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4111      * @param {EventObject} e The mouse down event
4112      * @return {Object} The dragData
4113      */
4114     getDragData : function(e){
4115         return Roo.dd.Registry.getHandleFromEvent(e);
4116     },
4117     
4118     /**
4119      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4120      * this.dragData.ddel
4121      * @param {Number} x The x position of the click on the dragged object
4122      * @param {Number} y The y position of the click on the dragged object
4123      * @return {Boolean} true to continue the drag, false to cancel
4124      */
4125     onInitDrag : function(x, y){
4126         this.proxy.update(this.dragData.ddel.cloneNode(true));
4127         this.onStartDrag(x, y);
4128         return true;
4129     },
4130     
4131     /**
4132      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4133      */
4134     afterRepair : function(){
4135         if(Roo.enableFx){
4136             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4137         }
4138         this.dragging = false;
4139     },
4140
4141     /**
4142      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4143      * the XY of this.dragData.ddel
4144      * @param {EventObject} e The mouse up event
4145      * @return {Array} The xy location (e.g. [100, 200])
4146      */
4147     getRepairXY : function(e){
4148         return Roo.Element.fly(this.dragData.ddel).getXY();  
4149     }
4150 });/*
4151  * Based on:
4152  * Ext JS Library 1.1.1
4153  * Copyright(c) 2006-2007, Ext JS, LLC.
4154  *
4155  * Originally Released Under LGPL - original licence link has changed is not relivant.
4156  *
4157  * Fork - LGPL
4158  * <script type="text/javascript">
4159  */
4160 /**
4161  * @class Roo.dd.DropZone
4162  * @extends Roo.dd.DropTarget
4163  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4164  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4165  * @constructor
4166  * @param {String/HTMLElement/Element} el The container element
4167  * @param {Object} config
4168  */
4169 Roo.dd.DropZone = function(el, config){
4170     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4171 };
4172
4173 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4174     /**
4175      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4176      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4177      * provide your own custom lookup.
4178      * @param {Event} e The event
4179      * @return {Object} data The custom data
4180      */
4181     getTargetFromEvent : function(e){
4182         return Roo.dd.Registry.getTargetFromEvent(e);
4183     },
4184
4185     /**
4186      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4187      * that it has registered.  This method has no default implementation and should be overridden to provide
4188      * node-specific processing if necessary.
4189      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4190      * {@link #getTargetFromEvent} for this node)
4191      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4192      * @param {Event} e The event
4193      * @param {Object} data An object containing arbitrary data supplied by the drag source
4194      */
4195     onNodeEnter : function(n, dd, e, data){
4196         
4197     },
4198
4199     /**
4200      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4201      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4202      * overridden to provide the proper feedback.
4203      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4204      * {@link #getTargetFromEvent} for this node)
4205      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4206      * @param {Event} e The event
4207      * @param {Object} data An object containing arbitrary data supplied by the drag source
4208      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4209      * underlying {@link Roo.dd.StatusProxy} can be updated
4210      */
4211     onNodeOver : function(n, dd, e, data){
4212         return this.dropAllowed;
4213     },
4214
4215     /**
4216      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4217      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4218      * node-specific processing if necessary.
4219      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4220      * {@link #getTargetFromEvent} for this node)
4221      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4222      * @param {Event} e The event
4223      * @param {Object} data An object containing arbitrary data supplied by the drag source
4224      */
4225     onNodeOut : function(n, dd, e, data){
4226         
4227     },
4228
4229     /**
4230      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4231      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4232      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4233      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4234      * {@link #getTargetFromEvent} for this node)
4235      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4236      * @param {Event} e The event
4237      * @param {Object} data An object containing arbitrary data supplied by the drag source
4238      * @return {Boolean} True if the drop was valid, else false
4239      */
4240     onNodeDrop : function(n, dd, e, data){
4241         return false;
4242     },
4243
4244     /**
4245      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4246      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4247      * it should be overridden to provide the proper feedback if necessary.
4248      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4249      * @param {Event} e The event
4250      * @param {Object} data An object containing arbitrary data supplied by the drag source
4251      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4252      * underlying {@link Roo.dd.StatusProxy} can be updated
4253      */
4254     onContainerOver : function(dd, e, data){
4255         return this.dropNotAllowed;
4256     },
4257
4258     /**
4259      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4260      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4261      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4262      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4263      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4264      * @param {Event} e The event
4265      * @param {Object} data An object containing arbitrary data supplied by the drag source
4266      * @return {Boolean} True if the drop was valid, else false
4267      */
4268     onContainerDrop : function(dd, e, data){
4269         return false;
4270     },
4271
4272     /**
4273      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4274      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4275      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4276      * you should override this method and provide a custom implementation.
4277      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4278      * @param {Event} e The event
4279      * @param {Object} data An object containing arbitrary data supplied by the drag source
4280      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4281      * underlying {@link Roo.dd.StatusProxy} can be updated
4282      */
4283     notifyEnter : function(dd, e, data){
4284         return this.dropNotAllowed;
4285     },
4286
4287     /**
4288      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4289      * This method will be called on every mouse movement while the drag source is over the drop zone.
4290      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4291      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4292      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4293      * registered node, it will call {@link #onContainerOver}.
4294      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4295      * @param {Event} e The event
4296      * @param {Object} data An object containing arbitrary data supplied by the drag source
4297      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4298      * underlying {@link Roo.dd.StatusProxy} can be updated
4299      */
4300     notifyOver : function(dd, e, data){
4301         var n = this.getTargetFromEvent(e);
4302         if(!n){ // not over valid drop target
4303             if(this.lastOverNode){
4304                 this.onNodeOut(this.lastOverNode, dd, e, data);
4305                 this.lastOverNode = null;
4306             }
4307             return this.onContainerOver(dd, e, data);
4308         }
4309         if(this.lastOverNode != n){
4310             if(this.lastOverNode){
4311                 this.onNodeOut(this.lastOverNode, dd, e, data);
4312             }
4313             this.onNodeEnter(n, dd, e, data);
4314             this.lastOverNode = n;
4315         }
4316         return this.onNodeOver(n, dd, e, data);
4317     },
4318
4319     /**
4320      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4321      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4322      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4323      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4324      * @param {Event} e The event
4325      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4326      */
4327     notifyOut : function(dd, e, data){
4328         if(this.lastOverNode){
4329             this.onNodeOut(this.lastOverNode, dd, e, data);
4330             this.lastOverNode = null;
4331         }
4332     },
4333
4334     /**
4335      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4336      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4337      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4338      * otherwise it will call {@link #onContainerDrop}.
4339      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4340      * @param {Event} e The event
4341      * @param {Object} data An object containing arbitrary data supplied by the drag source
4342      * @return {Boolean} True if the drop was valid, else false
4343      */
4344     notifyDrop : function(dd, e, data){
4345         if(this.lastOverNode){
4346             this.onNodeOut(this.lastOverNode, dd, e, data);
4347             this.lastOverNode = null;
4348         }
4349         var n = this.getTargetFromEvent(e);
4350         return n ?
4351             this.onNodeDrop(n, dd, e, data) :
4352             this.onContainerDrop(dd, e, data);
4353     },
4354
4355     // private
4356     triggerCacheRefresh : function(){
4357         Roo.dd.DDM.refreshCache(this.groups);
4358     }  
4359 });/*
4360  * Based on:
4361  * Ext JS Library 1.1.1
4362  * Copyright(c) 2006-2007, Ext JS, LLC.
4363  *
4364  * Originally Released Under LGPL - original licence link has changed is not relivant.
4365  *
4366  * Fork - LGPL
4367  * <script type="text/javascript">
4368  */
4369
4370
4371 /**
4372  * @class Roo.data.SortTypes
4373  * @singleton
4374  * Defines the default sorting (casting?) comparison functions used when sorting data.
4375  */
4376 Roo.data.SortTypes = {
4377     /**
4378      * Default sort that does nothing
4379      * @param {Mixed} s The value being converted
4380      * @return {Mixed} The comparison value
4381      */
4382     none : function(s){
4383         return s;
4384     },
4385     
4386     /**
4387      * The regular expression used to strip tags
4388      * @type {RegExp}
4389      * @property
4390      */
4391     stripTagsRE : /<\/?[^>]+>/gi,
4392     
4393     /**
4394      * Strips all HTML tags to sort on text only
4395      * @param {Mixed} s The value being converted
4396      * @return {String} The comparison value
4397      */
4398     asText : function(s){
4399         return String(s).replace(this.stripTagsRE, "");
4400     },
4401     
4402     /**
4403      * Strips all HTML tags to sort on text only - Case insensitive
4404      * @param {Mixed} s The value being converted
4405      * @return {String} The comparison value
4406      */
4407     asUCText : function(s){
4408         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4409     },
4410     
4411     /**
4412      * Case insensitive string
4413      * @param {Mixed} s The value being converted
4414      * @return {String} The comparison value
4415      */
4416     asUCString : function(s) {
4417         return String(s).toUpperCase();
4418     },
4419     
4420     /**
4421      * Date sorting
4422      * @param {Mixed} s The value being converted
4423      * @return {Number} The comparison value
4424      */
4425     asDate : function(s) {
4426         if(!s){
4427             return 0;
4428         }
4429         if(s instanceof Date){
4430             return s.getTime();
4431         }
4432         return Date.parse(String(s));
4433     },
4434     
4435     /**
4436      * Float sorting
4437      * @param {Mixed} s The value being converted
4438      * @return {Float} The comparison value
4439      */
4440     asFloat : function(s) {
4441         var val = parseFloat(String(s).replace(/,/g, ""));
4442         if(isNaN(val)) val = 0;
4443         return val;
4444     },
4445     
4446     /**
4447      * Integer sorting
4448      * @param {Mixed} s The value being converted
4449      * @return {Number} The comparison value
4450      */
4451     asInt : function(s) {
4452         var val = parseInt(String(s).replace(/,/g, ""));
4453         if(isNaN(val)) val = 0;
4454         return val;
4455     }
4456 };/*
4457  * Based on:
4458  * Ext JS Library 1.1.1
4459  * Copyright(c) 2006-2007, Ext JS, LLC.
4460  *
4461  * Originally Released Under LGPL - original licence link has changed is not relivant.
4462  *
4463  * Fork - LGPL
4464  * <script type="text/javascript">
4465  */
4466
4467 /**
4468 * @class Roo.data.Record
4469  * Instances of this class encapsulate both record <em>definition</em> information, and record
4470  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4471  * to access Records cached in an {@link Roo.data.Store} object.<br>
4472  * <p>
4473  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4474  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4475  * objects.<br>
4476  * <p>
4477  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4478  * @constructor
4479  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4480  * {@link #create}. The parameters are the same.
4481  * @param {Array} data An associative Array of data values keyed by the field name.
4482  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4483  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4484  * not specified an integer id is generated.
4485  */
4486 Roo.data.Record = function(data, id){
4487     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4488     this.data = data;
4489 };
4490
4491 /**
4492  * Generate a constructor for a specific record layout.
4493  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4494  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4495  * Each field definition object may contain the following properties: <ul>
4496  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
4497  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4498  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4499  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4500  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4501  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4502  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4503  * this may be omitted.</p></li>
4504  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4505  * <ul><li>auto (Default, implies no conversion)</li>
4506  * <li>string</li>
4507  * <li>int</li>
4508  * <li>float</li>
4509  * <li>boolean</li>
4510  * <li>date</li></ul></p></li>
4511  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4512  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4513  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4514  * by the Reader into an object that will be stored in the Record. It is passed the
4515  * following parameters:<ul>
4516  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4517  * </ul></p></li>
4518  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4519  * </ul>
4520  * <br>usage:<br><pre><code>
4521 var TopicRecord = Roo.data.Record.create(
4522     {name: 'title', mapping: 'topic_title'},
4523     {name: 'author', mapping: 'username'},
4524     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4525     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4526     {name: 'lastPoster', mapping: 'user2'},
4527     {name: 'excerpt', mapping: 'post_text'}
4528 );
4529
4530 var myNewRecord = new TopicRecord({
4531     title: 'Do my job please',
4532     author: 'noobie',
4533     totalPosts: 1,
4534     lastPost: new Date(),
4535     lastPoster: 'Animal',
4536     excerpt: 'No way dude!'
4537 });
4538 myStore.add(myNewRecord);
4539 </code></pre>
4540  * @method create
4541  * @static
4542  */
4543 Roo.data.Record.create = function(o){
4544     var f = function(){
4545         f.superclass.constructor.apply(this, arguments);
4546     };
4547     Roo.extend(f, Roo.data.Record);
4548     var p = f.prototype;
4549     p.fields = new Roo.util.MixedCollection(false, function(field){
4550         return field.name;
4551     });
4552     for(var i = 0, len = o.length; i < len; i++){
4553         p.fields.add(new Roo.data.Field(o[i]));
4554     }
4555     f.getField = function(name){
4556         return p.fields.get(name);  
4557     };
4558     return f;
4559 };
4560
4561 Roo.data.Record.AUTO_ID = 1000;
4562 Roo.data.Record.EDIT = 'edit';
4563 Roo.data.Record.REJECT = 'reject';
4564 Roo.data.Record.COMMIT = 'commit';
4565
4566 Roo.data.Record.prototype = {
4567     /**
4568      * Readonly flag - true if this record has been modified.
4569      * @type Boolean
4570      */
4571     dirty : false,
4572     editing : false,
4573     error: null,
4574     modified: null,
4575
4576     // private
4577     join : function(store){
4578         this.store = store;
4579     },
4580
4581     /**
4582      * Set the named field to the specified value.
4583      * @param {String} name The name of the field to set.
4584      * @param {Object} value The value to set the field to.
4585      */
4586     set : function(name, value){
4587         if(this.data[name] == value){
4588             return;
4589         }
4590         this.dirty = true;
4591         if(!this.modified){
4592             this.modified = {};
4593         }
4594         if(typeof this.modified[name] == 'undefined'){
4595             this.modified[name] = this.data[name];
4596         }
4597         this.data[name] = value;
4598         if(!this.editing && this.store){
4599             this.store.afterEdit(this);
4600         }       
4601     },
4602
4603     /**
4604      * Get the value of the named field.
4605      * @param {String} name The name of the field to get the value of.
4606      * @return {Object} The value of the field.
4607      */
4608     get : function(name){
4609         return this.data[name]; 
4610     },
4611
4612     // private
4613     beginEdit : function(){
4614         this.editing = true;
4615         this.modified = {}; 
4616     },
4617
4618     // private
4619     cancelEdit : function(){
4620         this.editing = false;
4621         delete this.modified;
4622     },
4623
4624     // private
4625     endEdit : function(){
4626         this.editing = false;
4627         if(this.dirty && this.store){
4628             this.store.afterEdit(this);
4629         }
4630     },
4631
4632     /**
4633      * Usually called by the {@link Roo.data.Store} which owns the Record.
4634      * Rejects all changes made to the Record since either creation, or the last commit operation.
4635      * Modified fields are reverted to their original values.
4636      * <p>
4637      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4638      * of reject operations.
4639      */
4640     reject : function(){
4641         var m = this.modified;
4642         for(var n in m){
4643             if(typeof m[n] != "function"){
4644                 this.data[n] = m[n];
4645             }
4646         }
4647         this.dirty = false;
4648         delete this.modified;
4649         this.editing = false;
4650         if(this.store){
4651             this.store.afterReject(this);
4652         }
4653     },
4654
4655     /**
4656      * Usually called by the {@link Roo.data.Store} which owns the Record.
4657      * Commits all changes made to the Record since either creation, or the last commit operation.
4658      * <p>
4659      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4660      * of commit operations.
4661      */
4662     commit : function(){
4663         this.dirty = false;
4664         delete this.modified;
4665         this.editing = false;
4666         if(this.store){
4667             this.store.afterCommit(this);
4668         }
4669     },
4670
4671     // private
4672     hasError : function(){
4673         return this.error != null;
4674     },
4675
4676     // private
4677     clearError : function(){
4678         this.error = null;
4679     },
4680
4681     /**
4682      * Creates a copy of this record.
4683      * @param {String} id (optional) A new record id if you don't want to use this record's id
4684      * @return {Record}
4685      */
4686     copy : function(newId) {
4687         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4688     }
4689 };/*
4690  * Based on:
4691  * Ext JS Library 1.1.1
4692  * Copyright(c) 2006-2007, Ext JS, LLC.
4693  *
4694  * Originally Released Under LGPL - original licence link has changed is not relivant.
4695  *
4696  * Fork - LGPL
4697  * <script type="text/javascript">
4698  */
4699
4700
4701
4702 /**
4703  * @class Roo.data.Store
4704  * @extends Roo.util.Observable
4705  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4706  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4707  * <p>
4708  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
4709  * has no knowledge of the format of the data returned by the Proxy.<br>
4710  * <p>
4711  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4712  * instances from the data object. These records are cached and made available through accessor functions.
4713  * @constructor
4714  * Creates a new Store.
4715  * @param {Object} config A config object containing the objects needed for the Store to access data,
4716  * and read the data into Records.
4717  */
4718 Roo.data.Store = function(config){
4719     this.data = new Roo.util.MixedCollection(false);
4720     this.data.getKey = function(o){
4721         return o.id;
4722     };
4723     this.baseParams = {};
4724     // private
4725     this.paramNames = {
4726         "start" : "start",
4727         "limit" : "limit",
4728         "sort" : "sort",
4729         "dir" : "dir",
4730         "multisort" : "_multisort"
4731     };
4732
4733     if(config && config.data){
4734         this.inlineData = config.data;
4735         delete config.data;
4736     }
4737
4738     Roo.apply(this, config);
4739     
4740     if(this.reader){ // reader passed
4741         this.reader = Roo.factory(this.reader, Roo.data);
4742         this.reader.xmodule = this.xmodule || false;
4743         if(!this.recordType){
4744             this.recordType = this.reader.recordType;
4745         }
4746         if(this.reader.onMetaChange){
4747             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4748         }
4749     }
4750
4751     if(this.recordType){
4752         this.fields = this.recordType.prototype.fields;
4753     }
4754     this.modified = [];
4755
4756     this.addEvents({
4757         /**
4758          * @event datachanged
4759          * Fires when the data cache has changed, and a widget which is using this Store
4760          * as a Record cache should refresh its view.
4761          * @param {Store} this
4762          */
4763         datachanged : true,
4764         /**
4765          * @event metachange
4766          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4767          * @param {Store} this
4768          * @param {Object} meta The JSON metadata
4769          */
4770         metachange : true,
4771         /**
4772          * @event add
4773          * Fires when Records have been added to the Store
4774          * @param {Store} this
4775          * @param {Roo.data.Record[]} records The array of Records added
4776          * @param {Number} index The index at which the record(s) were added
4777          */
4778         add : true,
4779         /**
4780          * @event remove
4781          * Fires when a Record has been removed from the Store
4782          * @param {Store} this
4783          * @param {Roo.data.Record} record The Record that was removed
4784          * @param {Number} index The index at which the record was removed
4785          */
4786         remove : true,
4787         /**
4788          * @event update
4789          * Fires when a Record has been updated
4790          * @param {Store} this
4791          * @param {Roo.data.Record} record The Record that was updated
4792          * @param {String} operation The update operation being performed.  Value may be one of:
4793          * <pre><code>
4794  Roo.data.Record.EDIT
4795  Roo.data.Record.REJECT
4796  Roo.data.Record.COMMIT
4797          * </code></pre>
4798          */
4799         update : true,
4800         /**
4801          * @event clear
4802          * Fires when the data cache has been cleared.
4803          * @param {Store} this
4804          */
4805         clear : true,
4806         /**
4807          * @event beforeload
4808          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4809          * the load action will be canceled.
4810          * @param {Store} this
4811          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4812          */
4813         beforeload : true,
4814         /**
4815          * @event load
4816          * Fires after a new set of Records has been loaded.
4817          * @param {Store} this
4818          * @param {Roo.data.Record[]} records The Records that were loaded
4819          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4820          */
4821         load : true,
4822         /**
4823          * @event loadexception
4824          * Fires if an exception occurs in the Proxy during loading.
4825          * Called with the signature of the Proxy's "loadexception" event.
4826          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4827          * 
4828          * @param {Proxy} 
4829          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4830          * @param {Object} load options 
4831          * @param {Object} jsonData from your request (normally this contains the Exception)
4832          */
4833         loadexception : true
4834     });
4835     
4836     if(this.proxy){
4837         this.proxy = Roo.factory(this.proxy, Roo.data);
4838         this.proxy.xmodule = this.xmodule || false;
4839         this.relayEvents(this.proxy,  ["loadexception"]);
4840     }
4841     this.sortToggle = {};
4842     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
4843
4844     Roo.data.Store.superclass.constructor.call(this);
4845
4846     if(this.inlineData){
4847         this.loadData(this.inlineData);
4848         delete this.inlineData;
4849     }
4850 };
4851 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4852      /**
4853     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4854     * without a remote query - used by combo/forms at present.
4855     */
4856     
4857     /**
4858     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4859     */
4860     /**
4861     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4862     */
4863     /**
4864     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4865     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4866     */
4867     /**
4868     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4869     * on any HTTP request
4870     */
4871     /**
4872     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4873     */
4874     /**
4875     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
4876     */
4877     multiSort: false,
4878     /**
4879     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4880     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4881     */
4882     remoteSort : false,
4883
4884     /**
4885     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4886      * loaded or when a record is removed. (defaults to false).
4887     */
4888     pruneModifiedRecords : false,
4889
4890     // private
4891     lastOptions : null,
4892
4893     /**
4894      * Add Records to the Store and fires the add event.
4895      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4896      */
4897     add : function(records){
4898         records = [].concat(records);
4899         for(var i = 0, len = records.length; i < len; i++){
4900             records[i].join(this);
4901         }
4902         var index = this.data.length;
4903         this.data.addAll(records);
4904         this.fireEvent("add", this, records, index);
4905     },
4906
4907     /**
4908      * Remove a Record from the Store and fires the remove event.
4909      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4910      */
4911     remove : function(record){
4912         var index = this.data.indexOf(record);
4913         this.data.removeAt(index);
4914         if(this.pruneModifiedRecords){
4915             this.modified.remove(record);
4916         }
4917         this.fireEvent("remove", this, record, index);
4918     },
4919
4920     /**
4921      * Remove all Records from the Store and fires the clear event.
4922      */
4923     removeAll : function(){
4924         this.data.clear();
4925         if(this.pruneModifiedRecords){
4926             this.modified = [];
4927         }
4928         this.fireEvent("clear", this);
4929     },
4930
4931     /**
4932      * Inserts Records to the Store at the given index and fires the add event.
4933      * @param {Number} index The start index at which to insert the passed Records.
4934      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4935      */
4936     insert : function(index, records){
4937         records = [].concat(records);
4938         for(var i = 0, len = records.length; i < len; i++){
4939             this.data.insert(index, records[i]);
4940             records[i].join(this);
4941         }
4942         this.fireEvent("add", this, records, index);
4943     },
4944
4945     /**
4946      * Get the index within the cache of the passed Record.
4947      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4948      * @return {Number} The index of the passed Record. Returns -1 if not found.
4949      */
4950     indexOf : function(record){
4951         return this.data.indexOf(record);
4952     },
4953
4954     /**
4955      * Get the index within the cache of the Record with the passed id.
4956      * @param {String} id The id of the Record to find.
4957      * @return {Number} The index of the Record. Returns -1 if not found.
4958      */
4959     indexOfId : function(id){
4960         return this.data.indexOfKey(id);
4961     },
4962
4963     /**
4964      * Get the Record with the specified id.
4965      * @param {String} id The id of the Record to find.
4966      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4967      */
4968     getById : function(id){
4969         return this.data.key(id);
4970     },
4971
4972     /**
4973      * Get the Record at the specified index.
4974      * @param {Number} index The index of the Record to find.
4975      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
4976      */
4977     getAt : function(index){
4978         return this.data.itemAt(index);
4979     },
4980
4981     /**
4982      * Returns a range of Records between specified indices.
4983      * @param {Number} startIndex (optional) The starting index (defaults to 0)
4984      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
4985      * @return {Roo.data.Record[]} An array of Records
4986      */
4987     getRange : function(start, end){
4988         return this.data.getRange(start, end);
4989     },
4990
4991     // private
4992     storeOptions : function(o){
4993         o = Roo.apply({}, o);
4994         delete o.callback;
4995         delete o.scope;
4996         this.lastOptions = o;
4997     },
4998
4999     /**
5000      * Loads the Record cache from the configured Proxy using the configured Reader.
5001      * <p>
5002      * If using remote paging, then the first load call must specify the <em>start</em>
5003      * and <em>limit</em> properties in the options.params property to establish the initial
5004      * position within the dataset, and the number of Records to cache on each read from the Proxy.
5005      * <p>
5006      * <strong>It is important to note that for remote data sources, loading is asynchronous,
5007      * and this call will return before the new data has been loaded. Perform any post-processing
5008      * in a callback function, or in a "load" event handler.</strong>
5009      * <p>
5010      * @param {Object} options An object containing properties which control loading options:<ul>
5011      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5012      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5013      * passed the following arguments:<ul>
5014      * <li>r : Roo.data.Record[]</li>
5015      * <li>options: Options object from the load call</li>
5016      * <li>success: Boolean success indicator</li></ul></li>
5017      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5018      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5019      * </ul>
5020      */
5021     load : function(options){
5022         options = options || {};
5023         if(this.fireEvent("beforeload", this, options) !== false){
5024             this.storeOptions(options);
5025             var p = Roo.apply(options.params || {}, this.baseParams);
5026             // if meta was not loaded from remote source.. try requesting it.
5027             if (!this.reader.metaFromRemote) {
5028                 p._requestMeta = 1;
5029             }
5030             if(this.sortInfo && this.remoteSort){
5031                 var pn = this.paramNames;
5032                 p[pn["sort"]] = this.sortInfo.field;
5033                 p[pn["dir"]] = this.sortInfo.direction;
5034             }
5035             if (this.multiSort) {
5036                 var pn = this.paramNames;
5037                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
5038             }
5039             
5040             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5041         }
5042     },
5043
5044     /**
5045      * Reloads the Record cache from the configured Proxy using the configured Reader and
5046      * the options from the last load operation performed.
5047      * @param {Object} options (optional) An object containing properties which may override the options
5048      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5049      * the most recently used options are reused).
5050      */
5051     reload : function(options){
5052         this.load(Roo.applyIf(options||{}, this.lastOptions));
5053     },
5054
5055     // private
5056     // Called as a callback by the Reader during a load operation.
5057     loadRecords : function(o, options, success){
5058         if(!o || success === false){
5059             if(success !== false){
5060                 this.fireEvent("load", this, [], options);
5061             }
5062             if(options.callback){
5063                 options.callback.call(options.scope || this, [], options, false);
5064             }
5065             return;
5066         }
5067         // if data returned failure - throw an exception.
5068         if (o.success === false) {
5069             // show a message if no listener is registered.
5070             if (!this.hasListener('loadexception') && typeof(this.reader.jsonData.errorMsg) != 'undefined') {
5071                     Roo.MessageBox.alert("Error loading",this.reader.jsonData.errorMsg);
5072             }
5073             // loadmask wil be hooked into this..
5074             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
5075             return;
5076         }
5077         var r = o.records, t = o.totalRecords || r.length;
5078         if(!options || options.add !== true){
5079             if(this.pruneModifiedRecords){
5080                 this.modified = [];
5081             }
5082             for(var i = 0, len = r.length; i < len; i++){
5083                 r[i].join(this);
5084             }
5085             if(this.snapshot){
5086                 this.data = this.snapshot;
5087                 delete this.snapshot;
5088             }
5089             this.data.clear();
5090             this.data.addAll(r);
5091             this.totalLength = t;
5092             this.applySort();
5093             this.fireEvent("datachanged", this);
5094         }else{
5095             this.totalLength = Math.max(t, this.data.length+r.length);
5096             this.add(r);
5097         }
5098         this.fireEvent("load", this, r, options);
5099         if(options.callback){
5100             options.callback.call(options.scope || this, r, options, true);
5101         }
5102     },
5103
5104
5105     /**
5106      * Loads data from a passed data block. A Reader which understands the format of the data
5107      * must have been configured in the constructor.
5108      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5109      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5110      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5111      */
5112     loadData : function(o, append){
5113         var r = this.reader.readRecords(o);
5114         this.loadRecords(r, {add: append}, true);
5115     },
5116
5117     /**
5118      * Gets the number of cached records.
5119      * <p>
5120      * <em>If using paging, this may not be the total size of the dataset. If the data object
5121      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5122      * the data set size</em>
5123      */
5124     getCount : function(){
5125         return this.data.length || 0;
5126     },
5127
5128     /**
5129      * Gets the total number of records in the dataset as returned by the server.
5130      * <p>
5131      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5132      * the dataset size</em>
5133      */
5134     getTotalCount : function(){
5135         return this.totalLength || 0;
5136     },
5137
5138     /**
5139      * Returns the sort state of the Store as an object with two properties:
5140      * <pre><code>
5141  field {String} The name of the field by which the Records are sorted
5142  direction {String} The sort order, "ASC" or "DESC"
5143      * </code></pre>
5144      */
5145     getSortState : function(){
5146         return this.sortInfo;
5147     },
5148
5149     // private
5150     applySort : function(){
5151         if(this.sortInfo && !this.remoteSort){
5152             var s = this.sortInfo, f = s.field;
5153             var st = this.fields.get(f).sortType;
5154             var fn = function(r1, r2){
5155                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5156                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5157             };
5158             this.data.sort(s.direction, fn);
5159             if(this.snapshot && this.snapshot != this.data){
5160                 this.snapshot.sort(s.direction, fn);
5161             }
5162         }
5163     },
5164
5165     /**
5166      * Sets the default sort column and order to be used by the next load operation.
5167      * @param {String} fieldName The name of the field to sort by.
5168      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5169      */
5170     setDefaultSort : function(field, dir){
5171         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5172     },
5173
5174     /**
5175      * Sort the Records.
5176      * If remote sorting is used, the sort is performed on the server, and the cache is
5177      * reloaded. If local sorting is used, the cache is sorted internally.
5178      * @param {String} fieldName The name of the field to sort by.
5179      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5180      */
5181     sort : function(fieldName, dir){
5182         var f = this.fields.get(fieldName);
5183         if(!dir){
5184             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
5185             
5186             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
5187                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5188             }else{
5189                 dir = f.sortDir;
5190             }
5191         }
5192         this.sortToggle[f.name] = dir;
5193         this.sortInfo = {field: f.name, direction: dir};
5194         if(!this.remoteSort){
5195             this.applySort();
5196             this.fireEvent("datachanged", this);
5197         }else{
5198             this.load(this.lastOptions);
5199         }
5200     },
5201
5202     /**
5203      * Calls the specified function for each of the Records in the cache.
5204      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5205      * Returning <em>false</em> aborts and exits the iteration.
5206      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5207      */
5208     each : function(fn, scope){
5209         this.data.each(fn, scope);
5210     },
5211
5212     /**
5213      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5214      * (e.g., during paging).
5215      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5216      */
5217     getModifiedRecords : function(){
5218         return this.modified;
5219     },
5220
5221     // private
5222     createFilterFn : function(property, value, anyMatch){
5223         if(!value.exec){ // not a regex
5224             value = String(value);
5225             if(value.length == 0){
5226                 return false;
5227             }
5228             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5229         }
5230         return function(r){
5231             return value.test(r.data[property]);
5232         };
5233     },
5234
5235     /**
5236      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5237      * @param {String} property A field on your records
5238      * @param {Number} start The record index to start at (defaults to 0)
5239      * @param {Number} end The last record index to include (defaults to length - 1)
5240      * @return {Number} The sum
5241      */
5242     sum : function(property, start, end){
5243         var rs = this.data.items, v = 0;
5244         start = start || 0;
5245         end = (end || end === 0) ? end : rs.length-1;
5246
5247         for(var i = start; i <= end; i++){
5248             v += (rs[i].data[property] || 0);
5249         }
5250         return v;
5251     },
5252
5253     /**
5254      * Filter the records by a specified property.
5255      * @param {String} field A field on your records
5256      * @param {String/RegExp} value Either a string that the field
5257      * should start with or a RegExp to test against the field
5258      * @param {Boolean} anyMatch True to match any part not just the beginning
5259      */
5260     filter : function(property, value, anyMatch){
5261         var fn = this.createFilterFn(property, value, anyMatch);
5262         return fn ? this.filterBy(fn) : this.clearFilter();
5263     },
5264
5265     /**
5266      * Filter by a function. The specified function will be called with each
5267      * record in this data source. If the function returns true the record is included,
5268      * otherwise it is filtered.
5269      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5270      * @param {Object} scope (optional) The scope of the function (defaults to this)
5271      */
5272     filterBy : function(fn, scope){
5273         this.snapshot = this.snapshot || this.data;
5274         this.data = this.queryBy(fn, scope||this);
5275         this.fireEvent("datachanged", this);
5276     },
5277
5278     /**
5279      * Query the records by a specified property.
5280      * @param {String} field A field on your records
5281      * @param {String/RegExp} value Either a string that the field
5282      * should start with or a RegExp to test against the field
5283      * @param {Boolean} anyMatch True to match any part not just the beginning
5284      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5285      */
5286     query : function(property, value, anyMatch){
5287         var fn = this.createFilterFn(property, value, anyMatch);
5288         return fn ? this.queryBy(fn) : this.data.clone();
5289     },
5290
5291     /**
5292      * Query by a function. The specified function will be called with each
5293      * record in this data source. If the function returns true the record is included
5294      * in the results.
5295      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5296      * @param {Object} scope (optional) The scope of the function (defaults to this)
5297       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5298      **/
5299     queryBy : function(fn, scope){
5300         var data = this.snapshot || this.data;
5301         return data.filterBy(fn, scope||this);
5302     },
5303
5304     /**
5305      * Collects unique values for a particular dataIndex from this store.
5306      * @param {String} dataIndex The property to collect
5307      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5308      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5309      * @return {Array} An array of the unique values
5310      **/
5311     collect : function(dataIndex, allowNull, bypassFilter){
5312         var d = (bypassFilter === true && this.snapshot) ?
5313                 this.snapshot.items : this.data.items;
5314         var v, sv, r = [], l = {};
5315         for(var i = 0, len = d.length; i < len; i++){
5316             v = d[i].data[dataIndex];
5317             sv = String(v);
5318             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5319                 l[sv] = true;
5320                 r[r.length] = v;
5321             }
5322         }
5323         return r;
5324     },
5325
5326     /**
5327      * Revert to a view of the Record cache with no filtering applied.
5328      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5329      */
5330     clearFilter : function(suppressEvent){
5331         if(this.snapshot && this.snapshot != this.data){
5332             this.data = this.snapshot;
5333             delete this.snapshot;
5334             if(suppressEvent !== true){
5335                 this.fireEvent("datachanged", this);
5336             }
5337         }
5338     },
5339
5340     // private
5341     afterEdit : function(record){
5342         if(this.modified.indexOf(record) == -1){
5343             this.modified.push(record);
5344         }
5345         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5346     },
5347     
5348     // private
5349     afterReject : function(record){
5350         this.modified.remove(record);
5351         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5352     },
5353
5354     // private
5355     afterCommit : function(record){
5356         this.modified.remove(record);
5357         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5358     },
5359
5360     /**
5361      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5362      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5363      */
5364     commitChanges : function(){
5365         var m = this.modified.slice(0);
5366         this.modified = [];
5367         for(var i = 0, len = m.length; i < len; i++){
5368             m[i].commit();
5369         }
5370     },
5371
5372     /**
5373      * Cancel outstanding changes on all changed records.
5374      */
5375     rejectChanges : function(){
5376         var m = this.modified.slice(0);
5377         this.modified = [];
5378         for(var i = 0, len = m.length; i < len; i++){
5379             m[i].reject();
5380         }
5381     },
5382
5383     onMetaChange : function(meta, rtype, o){
5384         this.recordType = rtype;
5385         this.fields = rtype.prototype.fields;
5386         delete this.snapshot;
5387         this.sortInfo = meta.sortInfo || this.sortInfo;
5388         this.modified = [];
5389         this.fireEvent('metachange', this, this.reader.meta);
5390     }
5391 });/*
5392  * Based on:
5393  * Ext JS Library 1.1.1
5394  * Copyright(c) 2006-2007, Ext JS, LLC.
5395  *
5396  * Originally Released Under LGPL - original licence link has changed is not relivant.
5397  *
5398  * Fork - LGPL
5399  * <script type="text/javascript">
5400  */
5401
5402 /**
5403  * @class Roo.data.SimpleStore
5404  * @extends Roo.data.Store
5405  * Small helper class to make creating Stores from Array data easier.
5406  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5407  * @cfg {Array} fields An array of field definition objects, or field name strings.
5408  * @cfg {Array} data The multi-dimensional array of data
5409  * @constructor
5410  * @param {Object} config
5411  */
5412 Roo.data.SimpleStore = function(config){
5413     Roo.data.SimpleStore.superclass.constructor.call(this, {
5414         isLocal : true,
5415         reader: new Roo.data.ArrayReader({
5416                 id: config.id
5417             },
5418             Roo.data.Record.create(config.fields)
5419         ),
5420         proxy : new Roo.data.MemoryProxy(config.data)
5421     });
5422     this.load();
5423 };
5424 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5425  * Based on:
5426  * Ext JS Library 1.1.1
5427  * Copyright(c) 2006-2007, Ext JS, LLC.
5428  *
5429  * Originally Released Under LGPL - original licence link has changed is not relivant.
5430  *
5431  * Fork - LGPL
5432  * <script type="text/javascript">
5433  */
5434
5435 /**
5436 /**
5437  * @extends Roo.data.Store
5438  * @class Roo.data.JsonStore
5439  * Small helper class to make creating Stores for JSON data easier. <br/>
5440 <pre><code>
5441 var store = new Roo.data.JsonStore({
5442     url: 'get-images.php',
5443     root: 'images',
5444     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5445 });
5446 </code></pre>
5447  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5448  * JsonReader and HttpProxy (unless inline data is provided).</b>
5449  * @cfg {Array} fields An array of field definition objects, or field name strings.
5450  * @constructor
5451  * @param {Object} config
5452  */
5453 Roo.data.JsonStore = function(c){
5454     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5455         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5456         reader: new Roo.data.JsonReader(c, c.fields)
5457     }));
5458 };
5459 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5460  * Based on:
5461  * Ext JS Library 1.1.1
5462  * Copyright(c) 2006-2007, Ext JS, LLC.
5463  *
5464  * Originally Released Under LGPL - original licence link has changed is not relivant.
5465  *
5466  * Fork - LGPL
5467  * <script type="text/javascript">
5468  */
5469
5470  
5471 Roo.data.Field = function(config){
5472     if(typeof config == "string"){
5473         config = {name: config};
5474     }
5475     Roo.apply(this, config);
5476     
5477     if(!this.type){
5478         this.type = "auto";
5479     }
5480     
5481     var st = Roo.data.SortTypes;
5482     // named sortTypes are supported, here we look them up
5483     if(typeof this.sortType == "string"){
5484         this.sortType = st[this.sortType];
5485     }
5486     
5487     // set default sortType for strings and dates
5488     if(!this.sortType){
5489         switch(this.type){
5490             case "string":
5491                 this.sortType = st.asUCString;
5492                 break;
5493             case "date":
5494                 this.sortType = st.asDate;
5495                 break;
5496             default:
5497                 this.sortType = st.none;
5498         }
5499     }
5500
5501     // define once
5502     var stripRe = /[\$,%]/g;
5503
5504     // prebuilt conversion function for this field, instead of
5505     // switching every time we're reading a value
5506     if(!this.convert){
5507         var cv, dateFormat = this.dateFormat;
5508         switch(this.type){
5509             case "":
5510             case "auto":
5511             case undefined:
5512                 cv = function(v){ return v; };
5513                 break;
5514             case "string":
5515                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5516                 break;
5517             case "int":
5518                 cv = function(v){
5519                     return v !== undefined && v !== null && v !== '' ?
5520                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5521                     };
5522                 break;
5523             case "float":
5524                 cv = function(v){
5525                     return v !== undefined && v !== null && v !== '' ?
5526                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5527                     };
5528                 break;
5529             case "bool":
5530             case "boolean":
5531                 cv = function(v){ return v === true || v === "true" || v == 1; };
5532                 break;
5533             case "date":
5534                 cv = function(v){
5535                     if(!v){
5536                         return '';
5537                     }
5538                     if(v instanceof Date){
5539                         return v;
5540                     }
5541                     if(dateFormat){
5542                         if(dateFormat == "timestamp"){
5543                             return new Date(v*1000);
5544                         }
5545                         return Date.parseDate(v, dateFormat);
5546                     }
5547                     var parsed = Date.parse(v);
5548                     return parsed ? new Date(parsed) : null;
5549                 };
5550              break;
5551             
5552         }
5553         this.convert = cv;
5554     }
5555 };
5556
5557 Roo.data.Field.prototype = {
5558     dateFormat: null,
5559     defaultValue: "",
5560     mapping: null,
5561     sortType : null,
5562     sortDir : "ASC"
5563 };/*
5564  * Based on:
5565  * Ext JS Library 1.1.1
5566  * Copyright(c) 2006-2007, Ext JS, LLC.
5567  *
5568  * Originally Released Under LGPL - original licence link has changed is not relivant.
5569  *
5570  * Fork - LGPL
5571  * <script type="text/javascript">
5572  */
5573  
5574 // Base class for reading structured data from a data source.  This class is intended to be
5575 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5576
5577 /**
5578  * @class Roo.data.DataReader
5579  * Base class for reading structured data from a data source.  This class is intended to be
5580  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5581  */
5582
5583 Roo.data.DataReader = function(meta, recordType){
5584     
5585     this.meta = meta;
5586     
5587     this.recordType = recordType instanceof Array ? 
5588         Roo.data.Record.create(recordType) : recordType;
5589 };
5590
5591 Roo.data.DataReader.prototype = {
5592      /**
5593      * Create an empty record
5594      * @param {Object} data (optional) - overlay some values
5595      * @return {Roo.data.Record} record created.
5596      */
5597     newRow :  function(d) {
5598         var da =  {};
5599         this.recordType.prototype.fields.each(function(c) {
5600             switch( c.type) {
5601                 case 'int' : da[c.name] = 0; break;
5602                 case 'date' : da[c.name] = new Date(); break;
5603                 case 'float' : da[c.name] = 0.0; break;
5604                 case 'boolean' : da[c.name] = false; break;
5605                 default : da[c.name] = ""; break;
5606             }
5607             
5608         });
5609         return new this.recordType(Roo.apply(da, d));
5610     }
5611     
5612 };/*
5613  * Based on:
5614  * Ext JS Library 1.1.1
5615  * Copyright(c) 2006-2007, Ext JS, LLC.
5616  *
5617  * Originally Released Under LGPL - original licence link has changed is not relivant.
5618  *
5619  * Fork - LGPL
5620  * <script type="text/javascript">
5621  */
5622
5623 /**
5624  * @class Roo.data.DataProxy
5625  * @extends Roo.data.Observable
5626  * This class is an abstract base class for implementations which provide retrieval of
5627  * unformatted data objects.<br>
5628  * <p>
5629  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5630  * (of the appropriate type which knows how to parse the data object) to provide a block of
5631  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5632  * <p>
5633  * Custom implementations must implement the load method as described in
5634  * {@link Roo.data.HttpProxy#load}.
5635  */
5636 Roo.data.DataProxy = function(){
5637     this.addEvents({
5638         /**
5639          * @event beforeload
5640          * Fires before a network request is made to retrieve a data object.
5641          * @param {Object} This DataProxy object.
5642          * @param {Object} params The params parameter to the load function.
5643          */
5644         beforeload : true,
5645         /**
5646          * @event load
5647          * Fires before the load method's callback is called.
5648          * @param {Object} This DataProxy object.
5649          * @param {Object} o The data object.
5650          * @param {Object} arg The callback argument object passed to the load function.
5651          */
5652         load : true,
5653         /**
5654          * @event loadexception
5655          * Fires if an Exception occurs during data retrieval.
5656          * @param {Object} This DataProxy object.
5657          * @param {Object} o The data object.
5658          * @param {Object} arg The callback argument object passed to the load function.
5659          * @param {Object} e The Exception.
5660          */
5661         loadexception : true
5662     });
5663     Roo.data.DataProxy.superclass.constructor.call(this);
5664 };
5665
5666 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5667
5668     /**
5669      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5670      */
5671 /*
5672  * Based on:
5673  * Ext JS Library 1.1.1
5674  * Copyright(c) 2006-2007, Ext JS, LLC.
5675  *
5676  * Originally Released Under LGPL - original licence link has changed is not relivant.
5677  *
5678  * Fork - LGPL
5679  * <script type="text/javascript">
5680  */
5681 /**
5682  * @class Roo.data.MemoryProxy
5683  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5684  * to the Reader when its load method is called.
5685  * @constructor
5686  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5687  */
5688 Roo.data.MemoryProxy = function(data){
5689     if (data.data) {
5690         data = data.data;
5691     }
5692     Roo.data.MemoryProxy.superclass.constructor.call(this);
5693     this.data = data;
5694 };
5695
5696 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5697     /**
5698      * Load data from the requested source (in this case an in-memory
5699      * data object passed to the constructor), read the data object into
5700      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5701      * process that block using the passed callback.
5702      * @param {Object} params This parameter is not used by the MemoryProxy class.
5703      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5704      * object into a block of Roo.data.Records.
5705      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5706      * The function must be passed <ul>
5707      * <li>The Record block object</li>
5708      * <li>The "arg" argument from the load function</li>
5709      * <li>A boolean success indicator</li>
5710      * </ul>
5711      * @param {Object} scope The scope in which to call the callback
5712      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5713      */
5714     load : function(params, reader, callback, scope, arg){
5715         params = params || {};
5716         var result;
5717         try {
5718             result = reader.readRecords(this.data);
5719         }catch(e){
5720             this.fireEvent("loadexception", this, arg, null, e);
5721             callback.call(scope, null, arg, false);
5722             return;
5723         }
5724         callback.call(scope, result, arg, true);
5725     },
5726     
5727     // private
5728     update : function(params, records){
5729         
5730     }
5731 });/*
5732  * Based on:
5733  * Ext JS Library 1.1.1
5734  * Copyright(c) 2006-2007, Ext JS, LLC.
5735  *
5736  * Originally Released Under LGPL - original licence link has changed is not relivant.
5737  *
5738  * Fork - LGPL
5739  * <script type="text/javascript">
5740  */
5741 /**
5742  * @class Roo.data.HttpProxy
5743  * @extends Roo.data.DataProxy
5744  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5745  * configured to reference a certain URL.<br><br>
5746  * <p>
5747  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5748  * from which the running page was served.<br><br>
5749  * <p>
5750  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5751  * <p>
5752  * Be aware that to enable the browser to parse an XML document, the server must set
5753  * the Content-Type header in the HTTP response to "text/xml".
5754  * @constructor
5755  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5756  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5757  * will be used to make the request.
5758  */
5759 Roo.data.HttpProxy = function(conn){
5760     Roo.data.HttpProxy.superclass.constructor.call(this);
5761     // is conn a conn config or a real conn?
5762     this.conn = conn;
5763     this.useAjax = !conn || !conn.events;
5764   
5765 };
5766
5767 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5768     // thse are take from connection...
5769     
5770     /**
5771      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5772      */
5773     /**
5774      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5775      * extra parameters to each request made by this object. (defaults to undefined)
5776      */
5777     /**
5778      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5779      *  to each request made by this object. (defaults to undefined)
5780      */
5781     /**
5782      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
5783      */
5784     /**
5785      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5786      */
5787      /**
5788      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5789      * @type Boolean
5790      */
5791   
5792
5793     /**
5794      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5795      * @type Boolean
5796      */
5797     /**
5798      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5799      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5800      * a finer-grained basis than the DataProxy events.
5801      */
5802     getConnection : function(){
5803         return this.useAjax ? Roo.Ajax : this.conn;
5804     },
5805
5806     /**
5807      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5808      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5809      * process that block using the passed callback.
5810      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5811      * for the request to the remote server.
5812      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5813      * object into a block of Roo.data.Records.
5814      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5815      * The function must be passed <ul>
5816      * <li>The Record block object</li>
5817      * <li>The "arg" argument from the load function</li>
5818      * <li>A boolean success indicator</li>
5819      * </ul>
5820      * @param {Object} scope The scope in which to call the callback
5821      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5822      */
5823     load : function(params, reader, callback, scope, arg){
5824         if(this.fireEvent("beforeload", this, params) !== false){
5825             var  o = {
5826                 params : params || {},
5827                 request: {
5828                     callback : callback,
5829                     scope : scope,
5830                     arg : arg
5831                 },
5832                 reader: reader,
5833                 callback : this.loadResponse,
5834                 scope: this
5835             };
5836             if(this.useAjax){
5837                 Roo.applyIf(o, this.conn);
5838                 if(this.activeRequest){
5839                     Roo.Ajax.abort(this.activeRequest);
5840                 }
5841                 this.activeRequest = Roo.Ajax.request(o);
5842             }else{
5843                 this.conn.request(o);
5844             }
5845         }else{
5846             callback.call(scope||this, null, arg, false);
5847         }
5848     },
5849
5850     // private
5851     loadResponse : function(o, success, response){
5852         delete this.activeRequest;
5853         if(!success){
5854             this.fireEvent("loadexception", this, o, response);
5855             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5856             return;
5857         }
5858         var result;
5859         try {
5860             result = o.reader.read(response);
5861         }catch(e){
5862             this.fireEvent("loadexception", this, o, response, e);
5863             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5864             return;
5865         }
5866         
5867         this.fireEvent("load", this, o, o.request.arg);
5868         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5869     },
5870
5871     // private
5872     update : function(dataSet){
5873
5874     },
5875
5876     // private
5877     updateResponse : function(dataSet){
5878
5879     }
5880 });/*
5881  * Based on:
5882  * Ext JS Library 1.1.1
5883  * Copyright(c) 2006-2007, Ext JS, LLC.
5884  *
5885  * Originally Released Under LGPL - original licence link has changed is not relivant.
5886  *
5887  * Fork - LGPL
5888  * <script type="text/javascript">
5889  */
5890
5891 /**
5892  * @class Roo.data.ScriptTagProxy
5893  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5894  * other than the originating domain of the running page.<br><br>
5895  * <p>
5896  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
5897  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5898  * <p>
5899  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5900  * source code that is used as the source inside a &lt;script> tag.<br><br>
5901  * <p>
5902  * In order for the browser to process the returned data, the server must wrap the data object
5903  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5904  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5905  * depending on whether the callback name was passed:
5906  * <p>
5907  * <pre><code>
5908 boolean scriptTag = false;
5909 String cb = request.getParameter("callback");
5910 if (cb != null) {
5911     scriptTag = true;
5912     response.setContentType("text/javascript");
5913 } else {
5914     response.setContentType("application/x-json");
5915 }
5916 Writer out = response.getWriter();
5917 if (scriptTag) {
5918     out.write(cb + "(");
5919 }
5920 out.print(dataBlock.toJsonString());
5921 if (scriptTag) {
5922     out.write(");");
5923 }
5924 </pre></code>
5925  *
5926  * @constructor
5927  * @param {Object} config A configuration object.
5928  */
5929 Roo.data.ScriptTagProxy = function(config){
5930     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5931     Roo.apply(this, config);
5932     this.head = document.getElementsByTagName("head")[0];
5933 };
5934
5935 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5936
5937 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5938     /**
5939      * @cfg {String} url The URL from which to request the data object.
5940      */
5941     /**
5942      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5943      */
5944     timeout : 30000,
5945     /**
5946      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5947      * the server the name of the callback function set up by the load call to process the returned data object.
5948      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5949      * javascript output which calls this named function passing the data object as its only parameter.
5950      */
5951     callbackParam : "callback",
5952     /**
5953      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5954      * name to the request.
5955      */
5956     nocache : true,
5957
5958     /**
5959      * Load data from the configured URL, read the data object into
5960      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5961      * process that block using the passed callback.
5962      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5963      * for the request to the remote server.
5964      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5965      * object into a block of Roo.data.Records.
5966      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5967      * The function must be passed <ul>
5968      * <li>The Record block object</li>
5969      * <li>The "arg" argument from the load function</li>
5970      * <li>A boolean success indicator</li>
5971      * </ul>
5972      * @param {Object} scope The scope in which to call the callback
5973      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5974      */
5975     load : function(params, reader, callback, scope, arg){
5976         if(this.fireEvent("beforeload", this, params) !== false){
5977
5978             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
5979
5980             var url = this.url;
5981             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
5982             if(this.nocache){
5983                 url += "&_dc=" + (new Date().getTime());
5984             }
5985             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
5986             var trans = {
5987                 id : transId,
5988                 cb : "stcCallback"+transId,
5989                 scriptId : "stcScript"+transId,
5990                 params : params,
5991                 arg : arg,
5992                 url : url,
5993                 callback : callback,
5994                 scope : scope,
5995                 reader : reader
5996             };
5997             var conn = this;
5998
5999             window[trans.cb] = function(o){
6000                 conn.handleResponse(o, trans);
6001             };
6002
6003             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
6004
6005             if(this.autoAbort !== false){
6006                 this.abort();
6007             }
6008
6009             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
6010
6011             var script = document.createElement("script");
6012             script.setAttribute("src", url);
6013             script.setAttribute("type", "text/javascript");
6014             script.setAttribute("id", trans.scriptId);
6015             this.head.appendChild(script);
6016
6017             this.trans = trans;
6018         }else{
6019             callback.call(scope||this, null, arg, false);
6020         }
6021     },
6022
6023     // private
6024     isLoading : function(){
6025         return this.trans ? true : false;
6026     },
6027
6028     /**
6029      * Abort the current server request.
6030      */
6031     abort : function(){
6032         if(this.isLoading()){
6033             this.destroyTrans(this.trans);
6034         }
6035     },
6036
6037     // private
6038     destroyTrans : function(trans, isLoaded){
6039         this.head.removeChild(document.getElementById(trans.scriptId));
6040         clearTimeout(trans.timeoutId);
6041         if(isLoaded){
6042             window[trans.cb] = undefined;
6043             try{
6044                 delete window[trans.cb];
6045             }catch(e){}
6046         }else{
6047             // if hasn't been loaded, wait for load to remove it to prevent script error
6048             window[trans.cb] = function(){
6049                 window[trans.cb] = undefined;
6050                 try{
6051                     delete window[trans.cb];
6052                 }catch(e){}
6053             };
6054         }
6055     },
6056
6057     // private
6058     handleResponse : function(o, trans){
6059         this.trans = false;
6060         this.destroyTrans(trans, true);
6061         var result;
6062         try {
6063             result = trans.reader.readRecords(o);
6064         }catch(e){
6065             this.fireEvent("loadexception", this, o, trans.arg, e);
6066             trans.callback.call(trans.scope||window, null, trans.arg, false);
6067             return;
6068         }
6069         this.fireEvent("load", this, o, trans.arg);
6070         trans.callback.call(trans.scope||window, result, trans.arg, true);
6071     },
6072
6073     // private
6074     handleFailure : function(trans){
6075         this.trans = false;
6076         this.destroyTrans(trans, false);
6077         this.fireEvent("loadexception", this, null, trans.arg);
6078         trans.callback.call(trans.scope||window, null, trans.arg, false);
6079     }
6080 });/*
6081  * Based on:
6082  * Ext JS Library 1.1.1
6083  * Copyright(c) 2006-2007, Ext JS, LLC.
6084  *
6085  * Originally Released Under LGPL - original licence link has changed is not relivant.
6086  *
6087  * Fork - LGPL
6088  * <script type="text/javascript">
6089  */
6090
6091 /**
6092  * @class Roo.data.JsonReader
6093  * @extends Roo.data.DataReader
6094  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6095  * based on mappings in a provided Roo.data.Record constructor.
6096  * 
6097  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6098  * in the reply previously. 
6099  * 
6100  * <p>
6101  * Example code:
6102  * <pre><code>
6103 var RecordDef = Roo.data.Record.create([
6104     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6105     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6106 ]);
6107 var myReader = new Roo.data.JsonReader({
6108     totalProperty: "results",    // The property which contains the total dataset size (optional)
6109     root: "rows",                // The property which contains an Array of row objects
6110     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6111 }, RecordDef);
6112 </code></pre>
6113  * <p>
6114  * This would consume a JSON file like this:
6115  * <pre><code>
6116 { 'results': 2, 'rows': [
6117     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6118     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6119 }
6120 </code></pre>
6121  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6122  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6123  * paged from the remote server.
6124  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6125  * @cfg {String} root name of the property which contains the Array of row objects.
6126  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6127  * @constructor
6128  * Create a new JsonReader
6129  * @param {Object} meta Metadata configuration options
6130  * @param {Object} recordType Either an Array of field definition objects,
6131  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6132  */
6133 Roo.data.JsonReader = function(meta, recordType){
6134     
6135     meta = meta || {};
6136     // set some defaults:
6137     Roo.applyIf(meta, {
6138         totalProperty: 'total',
6139         successProperty : 'success',
6140         root : 'data',
6141         id : 'id'
6142     });
6143     
6144     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6145 };
6146 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6147     
6148     /**
6149      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6150      * Used by Store query builder to append _requestMeta to params.
6151      * 
6152      */
6153     metaFromRemote : false,
6154     /**
6155      * This method is only used by a DataProxy which has retrieved data from a remote server.
6156      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6157      * @return {Object} data A data block which is used by an Roo.data.Store object as
6158      * a cache of Roo.data.Records.
6159      */
6160     read : function(response){
6161         var json = response.responseText;
6162        
6163         var o = /* eval:var:o */ eval("("+json+")");
6164         if(!o) {
6165             throw {message: "JsonReader.read: Json object not found"};
6166         }
6167         
6168         if(o.metaData){
6169             
6170             delete this.ef;
6171             this.metaFromRemote = true;
6172             this.meta = o.metaData;
6173             this.recordType = Roo.data.Record.create(o.metaData.fields);
6174             this.onMetaChange(this.meta, this.recordType, o);
6175         }
6176         return this.readRecords(o);
6177     },
6178
6179     // private function a store will implement
6180     onMetaChange : function(meta, recordType, o){
6181
6182     },
6183
6184     /**
6185          * @ignore
6186          */
6187     simpleAccess: function(obj, subsc) {
6188         return obj[subsc];
6189     },
6190
6191         /**
6192          * @ignore
6193          */
6194     getJsonAccessor: function(){
6195         var re = /[\[\.]/;
6196         return function(expr) {
6197             try {
6198                 return(re.test(expr))
6199                     ? new Function("obj", "return obj." + expr)
6200                     : function(obj){
6201                         return obj[expr];
6202                     };
6203             } catch(e){}
6204             return Roo.emptyFn;
6205         };
6206     }(),
6207
6208     /**
6209      * Create a data block containing Roo.data.Records from an XML document.
6210      * @param {Object} o An object which contains an Array of row objects in the property specified
6211      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6212      * which contains the total size of the dataset.
6213      * @return {Object} data A data block which is used by an Roo.data.Store object as
6214      * a cache of Roo.data.Records.
6215      */
6216     readRecords : function(o){
6217         /**
6218          * After any data loads, the raw JSON data is available for further custom processing.
6219          * @type Object
6220          */
6221         this.jsonData = o;
6222         var s = this.meta, Record = this.recordType,
6223             f = Record.prototype.fields, fi = f.items, fl = f.length;
6224
6225 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6226         if (!this.ef) {
6227             if(s.totalProperty) {
6228                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6229                 }
6230                 if(s.successProperty) {
6231                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6232                 }
6233                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6234                 if (s.id) {
6235                         var g = this.getJsonAccessor(s.id);
6236                         this.getId = function(rec) {
6237                                 var r = g(rec);
6238                                 return (r === undefined || r === "") ? null : r;
6239                         };
6240                 } else {
6241                         this.getId = function(){return null;};
6242                 }
6243             this.ef = [];
6244             for(var jj = 0; jj < fl; jj++){
6245                 f = fi[jj];
6246                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6247                 this.ef[jj] = this.getJsonAccessor(map);
6248             }
6249         }
6250
6251         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6252         if(s.totalProperty){
6253             var vt = parseInt(this.getTotal(o), 10);
6254             if(!isNaN(vt)){
6255                 totalRecords = vt;
6256             }
6257         }
6258         if(s.successProperty){
6259             var vs = this.getSuccess(o);
6260             if(vs === false || vs === 'false'){
6261                 success = false;
6262             }
6263         }
6264         var records = [];
6265             for(var i = 0; i < c; i++){
6266                     var n = root[i];
6267                 var values = {};
6268                 var id = this.getId(n);
6269                 for(var j = 0; j < fl; j++){
6270                     f = fi[j];
6271                 var v = this.ef[j](n);
6272                 if (!f.convert) {
6273                     Roo.log('missing convert for ' + f.name);
6274                     Roo.log(f);
6275                     continue;
6276                 }
6277                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6278                 }
6279                 var record = new Record(values, id);
6280                 record.json = n;
6281                 records[i] = record;
6282             }
6283             return {
6284                 success : success,
6285                 records : records,
6286                 totalRecords : totalRecords
6287             };
6288     }
6289 });/*
6290  * Based on:
6291  * Ext JS Library 1.1.1
6292  * Copyright(c) 2006-2007, Ext JS, LLC.
6293  *
6294  * Originally Released Under LGPL - original licence link has changed is not relivant.
6295  *
6296  * Fork - LGPL
6297  * <script type="text/javascript">
6298  */
6299
6300 /**
6301  * @class Roo.data.XmlReader
6302  * @extends Roo.data.DataReader
6303  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6304  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6305  * <p>
6306  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6307  * header in the HTTP response must be set to "text/xml".</em>
6308  * <p>
6309  * Example code:
6310  * <pre><code>
6311 var RecordDef = Roo.data.Record.create([
6312    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6313    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6314 ]);
6315 var myReader = new Roo.data.XmlReader({
6316    totalRecords: "results", // The element which contains the total dataset size (optional)
6317    record: "row",           // The repeated element which contains row information
6318    id: "id"                 // The element within the row that provides an ID for the record (optional)
6319 }, RecordDef);
6320 </code></pre>
6321  * <p>
6322  * This would consume an XML file like this:
6323  * <pre><code>
6324 &lt;?xml?>
6325 &lt;dataset>
6326  &lt;results>2&lt;/results>
6327  &lt;row>
6328    &lt;id>1&lt;/id>
6329    &lt;name>Bill&lt;/name>
6330    &lt;occupation>Gardener&lt;/occupation>
6331  &lt;/row>
6332  &lt;row>
6333    &lt;id>2&lt;/id>
6334    &lt;name>Ben&lt;/name>
6335    &lt;occupation>Horticulturalist&lt;/occupation>
6336  &lt;/row>
6337 &lt;/dataset>
6338 </code></pre>
6339  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6340  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6341  * paged from the remote server.
6342  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6343  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6344  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6345  * a record identifier value.
6346  * @constructor
6347  * Create a new XmlReader
6348  * @param {Object} meta Metadata configuration options
6349  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6350  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6351  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6352  */
6353 Roo.data.XmlReader = function(meta, recordType){
6354     meta = meta || {};
6355     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6356 };
6357 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6358     /**
6359      * This method is only used by a DataProxy which has retrieved data from a remote server.
6360          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6361          * to contain a method called 'responseXML' that returns an XML document object.
6362      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6363      * a cache of Roo.data.Records.
6364      */
6365     read : function(response){
6366         var doc = response.responseXML;
6367         if(!doc) {
6368             throw {message: "XmlReader.read: XML Document not available"};
6369         }
6370         return this.readRecords(doc);
6371     },
6372
6373     /**
6374      * Create a data block containing Roo.data.Records from an XML document.
6375          * @param {Object} doc A parsed XML document.
6376      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6377      * a cache of Roo.data.Records.
6378      */
6379     readRecords : function(doc){
6380         /**
6381          * After any data loads/reads, the raw XML Document is available for further custom processing.
6382          * @type XMLDocument
6383          */
6384         this.xmlData = doc;
6385         var root = doc.documentElement || doc;
6386         var q = Roo.DomQuery;
6387         var recordType = this.recordType, fields = recordType.prototype.fields;
6388         var sid = this.meta.id;
6389         var totalRecords = 0, success = true;
6390         if(this.meta.totalRecords){
6391             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6392         }
6393         
6394         if(this.meta.success){
6395             var sv = q.selectValue(this.meta.success, root, true);
6396             success = sv !== false && sv !== 'false';
6397         }
6398         var records = [];
6399         var ns = q.select(this.meta.record, root);
6400         for(var i = 0, len = ns.length; i < len; i++) {
6401                 var n = ns[i];
6402                 var values = {};
6403                 var id = sid ? q.selectValue(sid, n) : undefined;
6404                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6405                     var f = fields.items[j];
6406                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6407                     v = f.convert(v);
6408                     values[f.name] = v;
6409                 }
6410                 var record = new recordType(values, id);
6411                 record.node = n;
6412                 records[records.length] = record;
6413             }
6414
6415             return {
6416                 success : success,
6417                 records : records,
6418                 totalRecords : totalRecords || records.length
6419             };
6420     }
6421 });/*
6422  * Based on:
6423  * Ext JS Library 1.1.1
6424  * Copyright(c) 2006-2007, Ext JS, LLC.
6425  *
6426  * Originally Released Under LGPL - original licence link has changed is not relivant.
6427  *
6428  * Fork - LGPL
6429  * <script type="text/javascript">
6430  */
6431
6432 /**
6433  * @class Roo.data.ArrayReader
6434  * @extends Roo.data.DataReader
6435  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6436  * Each element of that Array represents a row of data fields. The
6437  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6438  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6439  * <p>
6440  * Example code:.
6441  * <pre><code>
6442 var RecordDef = Roo.data.Record.create([
6443     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6444     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6445 ]);
6446 var myReader = new Roo.data.ArrayReader({
6447     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6448 }, RecordDef);
6449 </code></pre>
6450  * <p>
6451  * This would consume an Array like this:
6452  * <pre><code>
6453 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6454   </code></pre>
6455  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6456  * @constructor
6457  * Create a new JsonReader
6458  * @param {Object} meta Metadata configuration options.
6459  * @param {Object} recordType Either an Array of field definition objects
6460  * as specified to {@link Roo.data.Record#create},
6461  * or an {@link Roo.data.Record} object
6462  * created using {@link Roo.data.Record#create}.
6463  */
6464 Roo.data.ArrayReader = function(meta, recordType){
6465     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6466 };
6467
6468 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6469     /**
6470      * Create a data block containing Roo.data.Records from an XML document.
6471      * @param {Object} o An Array of row objects which represents the dataset.
6472      * @return {Object} data A data block which is used by an Roo.data.Store object as
6473      * a cache of Roo.data.Records.
6474      */
6475     readRecords : function(o){
6476         var sid = this.meta ? this.meta.id : null;
6477         var recordType = this.recordType, fields = recordType.prototype.fields;
6478         var records = [];
6479         var root = o;
6480             for(var i = 0; i < root.length; i++){
6481                     var n = root[i];
6482                 var values = {};
6483                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6484                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6485                 var f = fields.items[j];
6486                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6487                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6488                 v = f.convert(v);
6489                 values[f.name] = v;
6490             }
6491                 var record = new recordType(values, id);
6492                 record.json = n;
6493                 records[records.length] = record;
6494             }
6495             return {
6496                 records : records,
6497                 totalRecords : records.length
6498             };
6499     }
6500 });/*
6501  * Based on:
6502  * Ext JS Library 1.1.1
6503  * Copyright(c) 2006-2007, Ext JS, LLC.
6504  *
6505  * Originally Released Under LGPL - original licence link has changed is not relivant.
6506  *
6507  * Fork - LGPL
6508  * <script type="text/javascript">
6509  */
6510
6511
6512 /**
6513  * @class Roo.data.Tree
6514  * @extends Roo.util.Observable
6515  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6516  * in the tree have most standard DOM functionality.
6517  * @constructor
6518  * @param {Node} root (optional) The root node
6519  */
6520 Roo.data.Tree = function(root){
6521    this.nodeHash = {};
6522    /**
6523     * The root node for this tree
6524     * @type Node
6525     */
6526    this.root = null;
6527    if(root){
6528        this.setRootNode(root);
6529    }
6530    this.addEvents({
6531        /**
6532         * @event append
6533         * Fires when a new child node is appended to a node in this tree.
6534         * @param {Tree} tree The owner tree
6535         * @param {Node} parent The parent node
6536         * @param {Node} node The newly appended node
6537         * @param {Number} index The index of the newly appended node
6538         */
6539        "append" : true,
6540        /**
6541         * @event remove
6542         * Fires when a child node is removed from a node in this tree.
6543         * @param {Tree} tree The owner tree
6544         * @param {Node} parent The parent node
6545         * @param {Node} node The child node removed
6546         */
6547        "remove" : true,
6548        /**
6549         * @event move
6550         * Fires when a node is moved to a new location in the tree
6551         * @param {Tree} tree The owner tree
6552         * @param {Node} node The node moved
6553         * @param {Node} oldParent The old parent of this node
6554         * @param {Node} newParent The new parent of this node
6555         * @param {Number} index The index it was moved to
6556         */
6557        "move" : true,
6558        /**
6559         * @event insert
6560         * Fires when a new child node is inserted in a node in this tree.
6561         * @param {Tree} tree The owner tree
6562         * @param {Node} parent The parent node
6563         * @param {Node} node The child node inserted
6564         * @param {Node} refNode The child node the node was inserted before
6565         */
6566        "insert" : true,
6567        /**
6568         * @event beforeappend
6569         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6570         * @param {Tree} tree The owner tree
6571         * @param {Node} parent The parent node
6572         * @param {Node} node The child node to be appended
6573         */
6574        "beforeappend" : true,
6575        /**
6576         * @event beforeremove
6577         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6578         * @param {Tree} tree The owner tree
6579         * @param {Node} parent The parent node
6580         * @param {Node} node The child node to be removed
6581         */
6582        "beforeremove" : true,
6583        /**
6584         * @event beforemove
6585         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6586         * @param {Tree} tree The owner tree
6587         * @param {Node} node The node being moved
6588         * @param {Node} oldParent The parent of the node
6589         * @param {Node} newParent The new parent the node is moving to
6590         * @param {Number} index The index it is being moved to
6591         */
6592        "beforemove" : true,
6593        /**
6594         * @event beforeinsert
6595         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6596         * @param {Tree} tree The owner tree
6597         * @param {Node} parent The parent node
6598         * @param {Node} node The child node to be inserted
6599         * @param {Node} refNode The child node the node is being inserted before
6600         */
6601        "beforeinsert" : true
6602    });
6603
6604     Roo.data.Tree.superclass.constructor.call(this);
6605 };
6606
6607 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6608     pathSeparator: "/",
6609
6610     proxyNodeEvent : function(){
6611         return this.fireEvent.apply(this, arguments);
6612     },
6613
6614     /**
6615      * Returns the root node for this tree.
6616      * @return {Node}
6617      */
6618     getRootNode : function(){
6619         return this.root;
6620     },
6621
6622     /**
6623      * Sets the root node for this tree.
6624      * @param {Node} node
6625      * @return {Node}
6626      */
6627     setRootNode : function(node){
6628         this.root = node;
6629         node.ownerTree = this;
6630         node.isRoot = true;
6631         this.registerNode(node);
6632         return node;
6633     },
6634
6635     /**
6636      * Gets a node in this tree by its id.
6637      * @param {String} id
6638      * @return {Node}
6639      */
6640     getNodeById : function(id){
6641         return this.nodeHash[id];
6642     },
6643
6644     registerNode : function(node){
6645         this.nodeHash[node.id] = node;
6646     },
6647
6648     unregisterNode : function(node){
6649         delete this.nodeHash[node.id];
6650     },
6651
6652     toString : function(){
6653         return "[Tree"+(this.id?" "+this.id:"")+"]";
6654     }
6655 });
6656
6657 /**
6658  * @class Roo.data.Node
6659  * @extends Roo.util.Observable
6660  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6661  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6662  * @constructor
6663  * @param {Object} attributes The attributes/config for the node
6664  */
6665 Roo.data.Node = function(attributes){
6666     /**
6667      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6668      * @type {Object}
6669      */
6670     this.attributes = attributes || {};
6671     this.leaf = this.attributes.leaf;
6672     /**
6673      * The node id. @type String
6674      */
6675     this.id = this.attributes.id;
6676     if(!this.id){
6677         this.id = Roo.id(null, "ynode-");
6678         this.attributes.id = this.id;
6679     }
6680      
6681     
6682     /**
6683      * All child nodes of this node. @type Array
6684      */
6685     this.childNodes = [];
6686     if(!this.childNodes.indexOf){ // indexOf is a must
6687         this.childNodes.indexOf = function(o){
6688             for(var i = 0, len = this.length; i < len; i++){
6689                 if(this[i] == o) {
6690                     return i;
6691                 }
6692             }
6693             return -1;
6694         };
6695     }
6696     /**
6697      * The parent node for this node. @type Node
6698      */
6699     this.parentNode = null;
6700     /**
6701      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6702      */
6703     this.firstChild = null;
6704     /**
6705      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6706      */
6707     this.lastChild = null;
6708     /**
6709      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6710      */
6711     this.previousSibling = null;
6712     /**
6713      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6714      */
6715     this.nextSibling = null;
6716
6717     this.addEvents({
6718        /**
6719         * @event append
6720         * Fires when a new child node is appended
6721         * @param {Tree} tree The owner tree
6722         * @param {Node} this This node
6723         * @param {Node} node The newly appended node
6724         * @param {Number} index The index of the newly appended node
6725         */
6726        "append" : true,
6727        /**
6728         * @event remove
6729         * Fires when a child node is removed
6730         * @param {Tree} tree The owner tree
6731         * @param {Node} this This node
6732         * @param {Node} node The removed node
6733         */
6734        "remove" : true,
6735        /**
6736         * @event move
6737         * Fires when this node is moved to a new location in the tree
6738         * @param {Tree} tree The owner tree
6739         * @param {Node} this This node
6740         * @param {Node} oldParent The old parent of this node
6741         * @param {Node} newParent The new parent of this node
6742         * @param {Number} index The index it was moved to
6743         */
6744        "move" : true,
6745        /**
6746         * @event insert
6747         * Fires when a new child node is inserted.
6748         * @param {Tree} tree The owner tree
6749         * @param {Node} this This node
6750         * @param {Node} node The child node inserted
6751         * @param {Node} refNode The child node the node was inserted before
6752         */
6753        "insert" : true,
6754        /**
6755         * @event beforeappend
6756         * Fires before a new child is appended, return false to cancel the append.
6757         * @param {Tree} tree The owner tree
6758         * @param {Node} this This node
6759         * @param {Node} node The child node to be appended
6760         */
6761        "beforeappend" : true,
6762        /**
6763         * @event beforeremove
6764         * Fires before a child is removed, return false to cancel the remove.
6765         * @param {Tree} tree The owner tree
6766         * @param {Node} this This node
6767         * @param {Node} node The child node to be removed
6768         */
6769        "beforeremove" : true,
6770        /**
6771         * @event beforemove
6772         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6773         * @param {Tree} tree The owner tree
6774         * @param {Node} this This node
6775         * @param {Node} oldParent The parent of this node
6776         * @param {Node} newParent The new parent this node is moving to
6777         * @param {Number} index The index it is being moved to
6778         */
6779        "beforemove" : true,
6780        /**
6781         * @event beforeinsert
6782         * Fires before a new child is inserted, return false to cancel the insert.
6783         * @param {Tree} tree The owner tree
6784         * @param {Node} this This node
6785         * @param {Node} node The child node to be inserted
6786         * @param {Node} refNode The child node the node is being inserted before
6787         */
6788        "beforeinsert" : true
6789    });
6790     this.listeners = this.attributes.listeners;
6791     Roo.data.Node.superclass.constructor.call(this);
6792 };
6793
6794 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6795     fireEvent : function(evtName){
6796         // first do standard event for this node
6797         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6798             return false;
6799         }
6800         // then bubble it up to the tree if the event wasn't cancelled
6801         var ot = this.getOwnerTree();
6802         if(ot){
6803             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6804                 return false;
6805             }
6806         }
6807         return true;
6808     },
6809
6810     /**
6811      * Returns true if this node is a leaf
6812      * @return {Boolean}
6813      */
6814     isLeaf : function(){
6815         return this.leaf === true;
6816     },
6817
6818     // private
6819     setFirstChild : function(node){
6820         this.firstChild = node;
6821     },
6822
6823     //private
6824     setLastChild : function(node){
6825         this.lastChild = node;
6826     },
6827
6828
6829     /**
6830      * Returns true if this node is the last child of its parent
6831      * @return {Boolean}
6832      */
6833     isLast : function(){
6834        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6835     },
6836
6837     /**
6838      * Returns true if this node is the first child of its parent
6839      * @return {Boolean}
6840      */
6841     isFirst : function(){
6842        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6843     },
6844
6845     hasChildNodes : function(){
6846         return !this.isLeaf() && this.childNodes.length > 0;
6847     },
6848
6849     /**
6850      * Insert node(s) as the last child node of this node.
6851      * @param {Node/Array} node The node or Array of nodes to append
6852      * @return {Node} The appended node if single append, or null if an array was passed
6853      */
6854     appendChild : function(node){
6855         var multi = false;
6856         if(node instanceof Array){
6857             multi = node;
6858         }else if(arguments.length > 1){
6859             multi = arguments;
6860         }
6861         // if passed an array or multiple args do them one by one
6862         if(multi){
6863             for(var i = 0, len = multi.length; i < len; i++) {
6864                 this.appendChild(multi[i]);
6865             }
6866         }else{
6867             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6868                 return false;
6869             }
6870             var index = this.childNodes.length;
6871             var oldParent = node.parentNode;
6872             // it's a move, make sure we move it cleanly
6873             if(oldParent){
6874                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6875                     return false;
6876                 }
6877                 oldParent.removeChild(node);
6878             }
6879             index = this.childNodes.length;
6880             if(index == 0){
6881                 this.setFirstChild(node);
6882             }
6883             this.childNodes.push(node);
6884             node.parentNode = this;
6885             var ps = this.childNodes[index-1];
6886             if(ps){
6887                 node.previousSibling = ps;
6888                 ps.nextSibling = node;
6889             }else{
6890                 node.previousSibling = null;
6891             }
6892             node.nextSibling = null;
6893             this.setLastChild(node);
6894             node.setOwnerTree(this.getOwnerTree());
6895             this.fireEvent("append", this.ownerTree, this, node, index);
6896             if(oldParent){
6897                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6898             }
6899             return node;
6900         }
6901     },
6902
6903     /**
6904      * Removes a child node from this node.
6905      * @param {Node} node The node to remove
6906      * @return {Node} The removed node
6907      */
6908     removeChild : function(node){
6909         var index = this.childNodes.indexOf(node);
6910         if(index == -1){
6911             return false;
6912         }
6913         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6914             return false;
6915         }
6916
6917         // remove it from childNodes collection
6918         this.childNodes.splice(index, 1);
6919
6920         // update siblings
6921         if(node.previousSibling){
6922             node.previousSibling.nextSibling = node.nextSibling;
6923         }
6924         if(node.nextSibling){
6925             node.nextSibling.previousSibling = node.previousSibling;
6926         }
6927
6928         // update child refs
6929         if(this.firstChild == node){
6930             this.setFirstChild(node.nextSibling);
6931         }
6932         if(this.lastChild == node){
6933             this.setLastChild(node.previousSibling);
6934         }
6935
6936         node.setOwnerTree(null);
6937         // clear any references from the node
6938         node.parentNode = null;
6939         node.previousSibling = null;
6940         node.nextSibling = null;
6941         this.fireEvent("remove", this.ownerTree, this, node);
6942         return node;
6943     },
6944
6945     /**
6946      * Inserts the first node before the second node in this nodes childNodes collection.
6947      * @param {Node} node The node to insert
6948      * @param {Node} refNode The node to insert before (if null the node is appended)
6949      * @return {Node} The inserted node
6950      */
6951     insertBefore : function(node, refNode){
6952         if(!refNode){ // like standard Dom, refNode can be null for append
6953             return this.appendChild(node);
6954         }
6955         // nothing to do
6956         if(node == refNode){
6957             return false;
6958         }
6959
6960         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6961             return false;
6962         }
6963         var index = this.childNodes.indexOf(refNode);
6964         var oldParent = node.parentNode;
6965         var refIndex = index;
6966
6967         // when moving internally, indexes will change after remove
6968         if(oldParent == this && this.childNodes.indexOf(node) < index){
6969             refIndex--;
6970         }
6971
6972         // it's a move, make sure we move it cleanly
6973         if(oldParent){
6974             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
6975                 return false;
6976             }
6977             oldParent.removeChild(node);
6978         }
6979         if(refIndex == 0){
6980             this.setFirstChild(node);
6981         }
6982         this.childNodes.splice(refIndex, 0, node);
6983         node.parentNode = this;
6984         var ps = this.childNodes[refIndex-1];
6985         if(ps){
6986             node.previousSibling = ps;
6987             ps.nextSibling = node;
6988         }else{
6989             node.previousSibling = null;
6990         }
6991         node.nextSibling = refNode;
6992         refNode.previousSibling = node;
6993         node.setOwnerTree(this.getOwnerTree());
6994         this.fireEvent("insert", this.ownerTree, this, node, refNode);
6995         if(oldParent){
6996             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
6997         }
6998         return node;
6999     },
7000
7001     /**
7002      * Returns the child node at the specified index.
7003      * @param {Number} index
7004      * @return {Node}
7005      */
7006     item : function(index){
7007         return this.childNodes[index];
7008     },
7009
7010     /**
7011      * Replaces one child node in this node with another.
7012      * @param {Node} newChild The replacement node
7013      * @param {Node} oldChild The node to replace
7014      * @return {Node} The replaced node
7015      */
7016     replaceChild : function(newChild, oldChild){
7017         this.insertBefore(newChild, oldChild);
7018         this.removeChild(oldChild);
7019         return oldChild;
7020     },
7021
7022     /**
7023      * Returns the index of a child node
7024      * @param {Node} node
7025      * @return {Number} The index of the node or -1 if it was not found
7026      */
7027     indexOf : function(child){
7028         return this.childNodes.indexOf(child);
7029     },
7030
7031     /**
7032      * Returns the tree this node is in.
7033      * @return {Tree}
7034      */
7035     getOwnerTree : function(){
7036         // if it doesn't have one, look for one
7037         if(!this.ownerTree){
7038             var p = this;
7039             while(p){
7040                 if(p.ownerTree){
7041                     this.ownerTree = p.ownerTree;
7042                     break;
7043                 }
7044                 p = p.parentNode;
7045             }
7046         }
7047         return this.ownerTree;
7048     },
7049
7050     /**
7051      * Returns depth of this node (the root node has a depth of 0)
7052      * @return {Number}
7053      */
7054     getDepth : function(){
7055         var depth = 0;
7056         var p = this;
7057         while(p.parentNode){
7058             ++depth;
7059             p = p.parentNode;
7060         }
7061         return depth;
7062     },
7063
7064     // private
7065     setOwnerTree : function(tree){
7066         // if it's move, we need to update everyone
7067         if(tree != this.ownerTree){
7068             if(this.ownerTree){
7069                 this.ownerTree.unregisterNode(this);
7070             }
7071             this.ownerTree = tree;
7072             var cs = this.childNodes;
7073             for(var i = 0, len = cs.length; i < len; i++) {
7074                 cs[i].setOwnerTree(tree);
7075             }
7076             if(tree){
7077                 tree.registerNode(this);
7078             }
7079         }
7080     },
7081
7082     /**
7083      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7084      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7085      * @return {String} The path
7086      */
7087     getPath : function(attr){
7088         attr = attr || "id";
7089         var p = this.parentNode;
7090         var b = [this.attributes[attr]];
7091         while(p){
7092             b.unshift(p.attributes[attr]);
7093             p = p.parentNode;
7094         }
7095         var sep = this.getOwnerTree().pathSeparator;
7096         return sep + b.join(sep);
7097     },
7098
7099     /**
7100      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7101      * function call will be the scope provided or the current node. The arguments to the function
7102      * will be the args provided or the current node. If the function returns false at any point,
7103      * the bubble is stopped.
7104      * @param {Function} fn The function to call
7105      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7106      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7107      */
7108     bubble : function(fn, scope, args){
7109         var p = this;
7110         while(p){
7111             if(fn.call(scope || p, args || p) === false){
7112                 break;
7113             }
7114             p = p.parentNode;
7115         }
7116     },
7117
7118     /**
7119      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7120      * function call will be the scope provided or the current node. The arguments to the function
7121      * will be the args provided or the current node. If the function returns false at any point,
7122      * the cascade is stopped on that branch.
7123      * @param {Function} fn The function to call
7124      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7125      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7126      */
7127     cascade : function(fn, scope, args){
7128         if(fn.call(scope || this, args || this) !== false){
7129             var cs = this.childNodes;
7130             for(var i = 0, len = cs.length; i < len; i++) {
7131                 cs[i].cascade(fn, scope, args);
7132             }
7133         }
7134     },
7135
7136     /**
7137      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7138      * function call will be the scope provided or the current node. The arguments to the function
7139      * will be the args provided or the current node. If the function returns false at any point,
7140      * the iteration stops.
7141      * @param {Function} fn The function to call
7142      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7143      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7144      */
7145     eachChild : function(fn, scope, args){
7146         var cs = this.childNodes;
7147         for(var i = 0, len = cs.length; i < len; i++) {
7148                 if(fn.call(scope || this, args || cs[i]) === false){
7149                     break;
7150                 }
7151         }
7152     },
7153
7154     /**
7155      * Finds the first child that has the attribute with the specified value.
7156      * @param {String} attribute The attribute name
7157      * @param {Mixed} value The value to search for
7158      * @return {Node} The found child or null if none was found
7159      */
7160     findChild : function(attribute, value){
7161         var cs = this.childNodes;
7162         for(var i = 0, len = cs.length; i < len; i++) {
7163                 if(cs[i].attributes[attribute] == value){
7164                     return cs[i];
7165                 }
7166         }
7167         return null;
7168     },
7169
7170     /**
7171      * Finds the first child by a custom function. The child matches if the function passed
7172      * returns true.
7173      * @param {Function} fn
7174      * @param {Object} scope (optional)
7175      * @return {Node} The found child or null if none was found
7176      */
7177     findChildBy : function(fn, scope){
7178         var cs = this.childNodes;
7179         for(var i = 0, len = cs.length; i < len; i++) {
7180                 if(fn.call(scope||cs[i], cs[i]) === true){
7181                     return cs[i];
7182                 }
7183         }
7184         return null;
7185     },
7186
7187     /**
7188      * Sorts this nodes children using the supplied sort function
7189      * @param {Function} fn
7190      * @param {Object} scope (optional)
7191      */
7192     sort : function(fn, scope){
7193         var cs = this.childNodes;
7194         var len = cs.length;
7195         if(len > 0){
7196             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7197             cs.sort(sortFn);
7198             for(var i = 0; i < len; i++){
7199                 var n = cs[i];
7200                 n.previousSibling = cs[i-1];
7201                 n.nextSibling = cs[i+1];
7202                 if(i == 0){
7203                     this.setFirstChild(n);
7204                 }
7205                 if(i == len-1){
7206                     this.setLastChild(n);
7207                 }
7208             }
7209         }
7210     },
7211
7212     /**
7213      * Returns true if this node is an ancestor (at any point) of the passed node.
7214      * @param {Node} node
7215      * @return {Boolean}
7216      */
7217     contains : function(node){
7218         return node.isAncestor(this);
7219     },
7220
7221     /**
7222      * Returns true if the passed node is an ancestor (at any point) of this node.
7223      * @param {Node} node
7224      * @return {Boolean}
7225      */
7226     isAncestor : function(node){
7227         var p = this.parentNode;
7228         while(p){
7229             if(p == node){
7230                 return true;
7231             }
7232             p = p.parentNode;
7233         }
7234         return false;
7235     },
7236
7237     toString : function(){
7238         return "[Node"+(this.id?" "+this.id:"")+"]";
7239     }
7240 });/*
7241  * Based on:
7242  * Ext JS Library 1.1.1
7243  * Copyright(c) 2006-2007, Ext JS, LLC.
7244  *
7245  * Originally Released Under LGPL - original licence link has changed is not relivant.
7246  *
7247  * Fork - LGPL
7248  * <script type="text/javascript">
7249  */
7250  
7251
7252 /**
7253  * @class Roo.ComponentMgr
7254  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7255  * @singleton
7256  */
7257 Roo.ComponentMgr = function(){
7258     var all = new Roo.util.MixedCollection();
7259
7260     return {
7261         /**
7262          * Registers a component.
7263          * @param {Roo.Component} c The component
7264          */
7265         register : function(c){
7266             all.add(c);
7267         },
7268
7269         /**
7270          * Unregisters a component.
7271          * @param {Roo.Component} c The component
7272          */
7273         unregister : function(c){
7274             all.remove(c);
7275         },
7276
7277         /**
7278          * Returns a component by id
7279          * @param {String} id The component id
7280          */
7281         get : function(id){
7282             return all.get(id);
7283         },
7284
7285         /**
7286          * Registers a function that will be called when a specified component is added to ComponentMgr
7287          * @param {String} id The component id
7288          * @param {Funtction} fn The callback function
7289          * @param {Object} scope The scope of the callback
7290          */
7291         onAvailable : function(id, fn, scope){
7292             all.on("add", function(index, o){
7293                 if(o.id == id){
7294                     fn.call(scope || o, o);
7295                     all.un("add", fn, scope);
7296                 }
7297             });
7298         }
7299     };
7300 }();/*
7301  * Based on:
7302  * Ext JS Library 1.1.1
7303  * Copyright(c) 2006-2007, Ext JS, LLC.
7304  *
7305  * Originally Released Under LGPL - original licence link has changed is not relivant.
7306  *
7307  * Fork - LGPL
7308  * <script type="text/javascript">
7309  */
7310  
7311 /**
7312  * @class Roo.Component
7313  * @extends Roo.util.Observable
7314  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7315  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7316  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7317  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7318  * All visual components (widgets) that require rendering into a layout should subclass Component.
7319  * @constructor
7320  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7321  * element and its id used as the component id.  If a string is passed, it is assumed to be the id of an existing element
7322  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7323  */
7324 Roo.Component = function(config){
7325     config = config || {};
7326     if(config.tagName || config.dom || typeof config == "string"){ // element object
7327         config = {el: config, id: config.id || config};
7328     }
7329     this.initialConfig = config;
7330
7331     Roo.apply(this, config);
7332     this.addEvents({
7333         /**
7334          * @event disable
7335          * Fires after the component is disabled.
7336              * @param {Roo.Component} this
7337              */
7338         disable : true,
7339         /**
7340          * @event enable
7341          * Fires after the component is enabled.
7342              * @param {Roo.Component} this
7343              */
7344         enable : true,
7345         /**
7346          * @event beforeshow
7347          * Fires before the component is shown.  Return false to stop the show.
7348              * @param {Roo.Component} this
7349              */
7350         beforeshow : true,
7351         /**
7352          * @event show
7353          * Fires after the component is shown.
7354              * @param {Roo.Component} this
7355              */
7356         show : true,
7357         /**
7358          * @event beforehide
7359          * Fires before the component is hidden. Return false to stop the hide.
7360              * @param {Roo.Component} this
7361              */
7362         beforehide : true,
7363         /**
7364          * @event hide
7365          * Fires after the component is hidden.
7366              * @param {Roo.Component} this
7367              */
7368         hide : true,
7369         /**
7370          * @event beforerender
7371          * Fires before the component is rendered. Return false to stop the render.
7372              * @param {Roo.Component} this
7373              */
7374         beforerender : true,
7375         /**
7376          * @event render
7377          * Fires after the component is rendered.
7378              * @param {Roo.Component} this
7379              */
7380         render : true,
7381         /**
7382          * @event beforedestroy
7383          * Fires before the component is destroyed. Return false to stop the destroy.
7384              * @param {Roo.Component} this
7385              */
7386         beforedestroy : true,
7387         /**
7388          * @event destroy
7389          * Fires after the component is destroyed.
7390              * @param {Roo.Component} this
7391              */
7392         destroy : true
7393     });
7394     if(!this.id){
7395         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7396     }
7397     Roo.ComponentMgr.register(this);
7398     Roo.Component.superclass.constructor.call(this);
7399     this.initComponent();
7400     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7401         this.render(this.renderTo);
7402         delete this.renderTo;
7403     }
7404 };
7405
7406 /** @private */
7407 Roo.Component.AUTO_ID = 1000;
7408
7409 Roo.extend(Roo.Component, Roo.util.Observable, {
7410     /**
7411      * @scope Roo.Component.prototype
7412      * @type {Boolean}
7413      * true if this component is hidden. Read-only.
7414      */
7415     hidden : false,
7416     /**
7417      * @type {Boolean}
7418      * true if this component is disabled. Read-only.
7419      */
7420     disabled : false,
7421     /**
7422      * @type {Boolean}
7423      * true if this component has been rendered. Read-only.
7424      */
7425     rendered : false,
7426     
7427     /** @cfg {String} disableClass
7428      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7429      */
7430     disabledClass : "x-item-disabled",
7431         /** @cfg {Boolean} allowDomMove
7432          * Whether the component can move the Dom node when rendering (defaults to true).
7433          */
7434     allowDomMove : true,
7435     /** @cfg {String} hideMode
7436      * How this component should hidden. Supported values are
7437      * "visibility" (css visibility), "offsets" (negative offset position) and
7438      * "display" (css display) - defaults to "display".
7439      */
7440     hideMode: 'display',
7441
7442     /** @private */
7443     ctype : "Roo.Component",
7444
7445     /**
7446      * @cfg {String} actionMode 
7447      * which property holds the element that used for  hide() / show() / disable() / enable()
7448      * default is 'el' 
7449      */
7450     actionMode : "el",
7451
7452     /** @private */
7453     getActionEl : function(){
7454         return this[this.actionMode];
7455     },
7456
7457     initComponent : Roo.emptyFn,
7458     /**
7459      * If this is a lazy rendering component, render it to its container element.
7460      * @param {String/HTMLElement/Element} container (optional) The element this component should be rendered into. If it is being applied to existing markup, this should be left off.
7461      */
7462     render : function(container, position){
7463         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7464             if(!container && this.el){
7465                 this.el = Roo.get(this.el);
7466                 container = this.el.dom.parentNode;
7467                 this.allowDomMove = false;
7468             }
7469             this.container = Roo.get(container);
7470             this.rendered = true;
7471             if(position !== undefined){
7472                 if(typeof position == 'number'){
7473                     position = this.container.dom.childNodes[position];
7474                 }else{
7475                     position = Roo.getDom(position);
7476                 }
7477             }
7478             this.onRender(this.container, position || null);
7479             if(this.cls){
7480                 this.el.addClass(this.cls);
7481                 delete this.cls;
7482             }
7483             if(this.style){
7484                 this.el.applyStyles(this.style);
7485                 delete this.style;
7486             }
7487             this.fireEvent("render", this);
7488             this.afterRender(this.container);
7489             if(this.hidden){
7490                 this.hide();
7491             }
7492             if(this.disabled){
7493                 this.disable();
7494             }
7495         }
7496         return this;
7497     },
7498
7499     /** @private */
7500     // default function is not really useful
7501     onRender : function(ct, position){
7502         if(this.el){
7503             this.el = Roo.get(this.el);
7504             if(this.allowDomMove !== false){
7505                 ct.dom.insertBefore(this.el.dom, position);
7506             }
7507         }
7508     },
7509
7510     /** @private */
7511     getAutoCreate : function(){
7512         var cfg = typeof this.autoCreate == "object" ?
7513                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7514         if(this.id && !cfg.id){
7515             cfg.id = this.id;
7516         }
7517         return cfg;
7518     },
7519
7520     /** @private */
7521     afterRender : Roo.emptyFn,
7522
7523     /**
7524      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7525      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7526      */
7527     destroy : function(){
7528         if(this.fireEvent("beforedestroy", this) !== false){
7529             this.purgeListeners();
7530             this.beforeDestroy();
7531             if(this.rendered){
7532                 this.el.removeAllListeners();
7533                 this.el.remove();
7534                 if(this.actionMode == "container"){
7535                     this.container.remove();
7536                 }
7537             }
7538             this.onDestroy();
7539             Roo.ComponentMgr.unregister(this);
7540             this.fireEvent("destroy", this);
7541         }
7542     },
7543
7544         /** @private */
7545     beforeDestroy : function(){
7546
7547     },
7548
7549         /** @private */
7550         onDestroy : function(){
7551
7552     },
7553
7554     /**
7555      * Returns the underlying {@link Roo.Element}.
7556      * @return {Roo.Element} The element
7557      */
7558     getEl : function(){
7559         return this.el;
7560     },
7561
7562     /**
7563      * Returns the id of this component.
7564      * @return {String}
7565      */
7566     getId : function(){
7567         return this.id;
7568     },
7569
7570     /**
7571      * Try to focus this component.
7572      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7573      * @return {Roo.Component} this
7574      */
7575     focus : function(selectText){
7576         if(this.rendered){
7577             this.el.focus();
7578             if(selectText === true){
7579                 this.el.dom.select();
7580             }
7581         }
7582         return this;
7583     },
7584
7585     /** @private */
7586     blur : function(){
7587         if(this.rendered){
7588             this.el.blur();
7589         }
7590         return this;
7591     },
7592
7593     /**
7594      * Disable this component.
7595      * @return {Roo.Component} this
7596      */
7597     disable : function(){
7598         if(this.rendered){
7599             this.onDisable();
7600         }
7601         this.disabled = true;
7602         this.fireEvent("disable", this);
7603         return this;
7604     },
7605
7606         // private
7607     onDisable : function(){
7608         this.getActionEl().addClass(this.disabledClass);
7609         this.el.dom.disabled = true;
7610     },
7611
7612     /**
7613      * Enable this component.
7614      * @return {Roo.Component} this
7615      */
7616     enable : function(){
7617         if(this.rendered){
7618             this.onEnable();
7619         }
7620         this.disabled = false;
7621         this.fireEvent("enable", this);
7622         return this;
7623     },
7624
7625         // private
7626     onEnable : function(){
7627         this.getActionEl().removeClass(this.disabledClass);
7628         this.el.dom.disabled = false;
7629     },
7630
7631     /**
7632      * Convenience function for setting disabled/enabled by boolean.
7633      * @param {Boolean} disabled
7634      */
7635     setDisabled : function(disabled){
7636         this[disabled ? "disable" : "enable"]();
7637     },
7638
7639     /**
7640      * Show this component.
7641      * @return {Roo.Component} this
7642      */
7643     show: function(){
7644         if(this.fireEvent("beforeshow", this) !== false){
7645             this.hidden = false;
7646             if(this.rendered){
7647                 this.onShow();
7648             }
7649             this.fireEvent("show", this);
7650         }
7651         return this;
7652     },
7653
7654     // private
7655     onShow : function(){
7656         var ae = this.getActionEl();
7657         if(this.hideMode == 'visibility'){
7658             ae.dom.style.visibility = "visible";
7659         }else if(this.hideMode == 'offsets'){
7660             ae.removeClass('x-hidden');
7661         }else{
7662             ae.dom.style.display = "";
7663         }
7664     },
7665
7666     /**
7667      * Hide this component.
7668      * @return {Roo.Component} this
7669      */
7670     hide: function(){
7671         if(this.fireEvent("beforehide", this) !== false){
7672             this.hidden = true;
7673             if(this.rendered){
7674                 this.onHide();
7675             }
7676             this.fireEvent("hide", this);
7677         }
7678         return this;
7679     },
7680
7681     // private
7682     onHide : function(){
7683         var ae = this.getActionEl();
7684         if(this.hideMode == 'visibility'){
7685             ae.dom.style.visibility = "hidden";
7686         }else if(this.hideMode == 'offsets'){
7687             ae.addClass('x-hidden');
7688         }else{
7689             ae.dom.style.display = "none";
7690         }
7691     },
7692
7693     /**
7694      * Convenience function to hide or show this component by boolean.
7695      * @param {Boolean} visible True to show, false to hide
7696      * @return {Roo.Component} this
7697      */
7698     setVisible: function(visible){
7699         if(visible) {
7700             this.show();
7701         }else{
7702             this.hide();
7703         }
7704         return this;
7705     },
7706
7707     /**
7708      * Returns true if this component is visible.
7709      */
7710     isVisible : function(){
7711         return this.getActionEl().isVisible();
7712     },
7713
7714     cloneConfig : function(overrides){
7715         overrides = overrides || {};
7716         var id = overrides.id || Roo.id();
7717         var cfg = Roo.applyIf(overrides, this.initialConfig);
7718         cfg.id = id; // prevent dup id
7719         return new this.constructor(cfg);
7720     }
7721 });/*
7722  * Based on:
7723  * Ext JS Library 1.1.1
7724  * Copyright(c) 2006-2007, Ext JS, LLC.
7725  *
7726  * Originally Released Under LGPL - original licence link has changed is not relivant.
7727  *
7728  * Fork - LGPL
7729  * <script type="text/javascript">
7730  */
7731  (function(){ 
7732 /**
7733  * @class Roo.Layer
7734  * @extends Roo.Element
7735  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7736  * automatic maintaining of shadow/shim positions.
7737  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7738  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7739  * you can pass a string with a CSS class name. False turns off the shadow.
7740  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7741  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7742  * @cfg {String} cls CSS class to add to the element
7743  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7744  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7745  * @constructor
7746  * @param {Object} config An object with config options.
7747  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7748  */
7749
7750 Roo.Layer = function(config, existingEl){
7751     config = config || {};
7752     var dh = Roo.DomHelper;
7753     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7754     if(existingEl){
7755         this.dom = Roo.getDom(existingEl);
7756     }
7757     if(!this.dom){
7758         var o = config.dh || {tag: "div", cls: "x-layer"};
7759         this.dom = dh.append(pel, o);
7760     }
7761     if(config.cls){
7762         this.addClass(config.cls);
7763     }
7764     this.constrain = config.constrain !== false;
7765     this.visibilityMode = Roo.Element.VISIBILITY;
7766     if(config.id){
7767         this.id = this.dom.id = config.id;
7768     }else{
7769         this.id = Roo.id(this.dom);
7770     }
7771     this.zindex = config.zindex || this.getZIndex();
7772     this.position("absolute", this.zindex);
7773     if(config.shadow){
7774         this.shadowOffset = config.shadowOffset || 4;
7775         this.shadow = new Roo.Shadow({
7776             offset : this.shadowOffset,
7777             mode : config.shadow
7778         });
7779     }else{
7780         this.shadowOffset = 0;
7781     }
7782     this.useShim = config.shim !== false && Roo.useShims;
7783     this.useDisplay = config.useDisplay;
7784     this.hide();
7785 };
7786
7787 var supr = Roo.Element.prototype;
7788
7789 // shims are shared among layer to keep from having 100 iframes
7790 var shims = [];
7791
7792 Roo.extend(Roo.Layer, Roo.Element, {
7793
7794     getZIndex : function(){
7795         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7796     },
7797
7798     getShim : function(){
7799         if(!this.useShim){
7800             return null;
7801         }
7802         if(this.shim){
7803             return this.shim;
7804         }
7805         var shim = shims.shift();
7806         if(!shim){
7807             shim = this.createShim();
7808             shim.enableDisplayMode('block');
7809             shim.dom.style.display = 'none';
7810             shim.dom.style.visibility = 'visible';
7811         }
7812         var pn = this.dom.parentNode;
7813         if(shim.dom.parentNode != pn){
7814             pn.insertBefore(shim.dom, this.dom);
7815         }
7816         shim.setStyle('z-index', this.getZIndex()-2);
7817         this.shim = shim;
7818         return shim;
7819     },
7820
7821     hideShim : function(){
7822         if(this.shim){
7823             this.shim.setDisplayed(false);
7824             shims.push(this.shim);
7825             delete this.shim;
7826         }
7827     },
7828
7829     disableShadow : function(){
7830         if(this.shadow){
7831             this.shadowDisabled = true;
7832             this.shadow.hide();
7833             this.lastShadowOffset = this.shadowOffset;
7834             this.shadowOffset = 0;
7835         }
7836     },
7837
7838     enableShadow : function(show){
7839         if(this.shadow){
7840             this.shadowDisabled = false;
7841             this.shadowOffset = this.lastShadowOffset;
7842             delete this.lastShadowOffset;
7843             if(show){
7844                 this.sync(true);
7845             }
7846         }
7847     },
7848
7849     // private
7850     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7851     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7852     sync : function(doShow){
7853         var sw = this.shadow;
7854         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7855             var sh = this.getShim();
7856
7857             var w = this.getWidth(),
7858                 h = this.getHeight();
7859
7860             var l = this.getLeft(true),
7861                 t = this.getTop(true);
7862
7863             if(sw && !this.shadowDisabled){
7864                 if(doShow && !sw.isVisible()){
7865                     sw.show(this);
7866                 }else{
7867                     sw.realign(l, t, w, h);
7868                 }
7869                 if(sh){
7870                     if(doShow){
7871                        sh.show();
7872                     }
7873                     // fit the shim behind the shadow, so it is shimmed too
7874                     var a = sw.adjusts, s = sh.dom.style;
7875                     s.left = (Math.min(l, l+a.l))+"px";
7876                     s.top = (Math.min(t, t+a.t))+"px";
7877                     s.width = (w+a.w)+"px";
7878                     s.height = (h+a.h)+"px";
7879                 }
7880             }else if(sh){
7881                 if(doShow){
7882                    sh.show();
7883                 }
7884                 sh.setSize(w, h);
7885                 sh.setLeftTop(l, t);
7886             }
7887             
7888         }
7889     },
7890
7891     // private
7892     destroy : function(){
7893         this.hideShim();
7894         if(this.shadow){
7895             this.shadow.hide();
7896         }
7897         this.removeAllListeners();
7898         var pn = this.dom.parentNode;
7899         if(pn){
7900             pn.removeChild(this.dom);
7901         }
7902         Roo.Element.uncache(this.id);
7903     },
7904
7905     remove : function(){
7906         this.destroy();
7907     },
7908
7909     // private
7910     beginUpdate : function(){
7911         this.updating = true;
7912     },
7913
7914     // private
7915     endUpdate : function(){
7916         this.updating = false;
7917         this.sync(true);
7918     },
7919
7920     // private
7921     hideUnders : function(negOffset){
7922         if(this.shadow){
7923             this.shadow.hide();
7924         }
7925         this.hideShim();
7926     },
7927
7928     // private
7929     constrainXY : function(){
7930         if(this.constrain){
7931             var vw = Roo.lib.Dom.getViewWidth(),
7932                 vh = Roo.lib.Dom.getViewHeight();
7933             var s = Roo.get(document).getScroll();
7934
7935             var xy = this.getXY();
7936             var x = xy[0], y = xy[1];   
7937             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7938             // only move it if it needs it
7939             var moved = false;
7940             // first validate right/bottom
7941             if((x + w) > vw+s.left){
7942                 x = vw - w - this.shadowOffset;
7943                 moved = true;
7944             }
7945             if((y + h) > vh+s.top){
7946                 y = vh - h - this.shadowOffset;
7947                 moved = true;
7948             }
7949             // then make sure top/left isn't negative
7950             if(x < s.left){
7951                 x = s.left;
7952                 moved = true;
7953             }
7954             if(y < s.top){
7955                 y = s.top;
7956                 moved = true;
7957             }
7958             if(moved){
7959                 if(this.avoidY){
7960                     var ay = this.avoidY;
7961                     if(y <= ay && (y+h) >= ay){
7962                         y = ay-h-5;   
7963                     }
7964                 }
7965                 xy = [x, y];
7966                 this.storeXY(xy);
7967                 supr.setXY.call(this, xy);
7968                 this.sync();
7969             }
7970         }
7971     },
7972
7973     isVisible : function(){
7974         return this.visible;    
7975     },
7976
7977     // private
7978     showAction : function(){
7979         this.visible = true; // track visibility to prevent getStyle calls
7980         if(this.useDisplay === true){
7981             this.setDisplayed("");
7982         }else if(this.lastXY){
7983             supr.setXY.call(this, this.lastXY);
7984         }else if(this.lastLT){
7985             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7986         }
7987     },
7988
7989     // private
7990     hideAction : function(){
7991         this.visible = false;
7992         if(this.useDisplay === true){
7993             this.setDisplayed(false);
7994         }else{
7995             this.setLeftTop(-10000,-10000);
7996         }
7997     },
7998
7999     // overridden Element method
8000     setVisible : function(v, a, d, c, e){
8001         if(v){
8002             this.showAction();
8003         }
8004         if(a && v){
8005             var cb = function(){
8006                 this.sync(true);
8007                 if(c){
8008                     c();
8009                 }
8010             }.createDelegate(this);
8011             supr.setVisible.call(this, true, true, d, cb, e);
8012         }else{
8013             if(!v){
8014                 this.hideUnders(true);
8015             }
8016             var cb = c;
8017             if(a){
8018                 cb = function(){
8019                     this.hideAction();
8020                     if(c){
8021                         c();
8022                     }
8023                 }.createDelegate(this);
8024             }
8025             supr.setVisible.call(this, v, a, d, cb, e);
8026             if(v){
8027                 this.sync(true);
8028             }else if(!a){
8029                 this.hideAction();
8030             }
8031         }
8032     },
8033
8034     storeXY : function(xy){
8035         delete this.lastLT;
8036         this.lastXY = xy;
8037     },
8038
8039     storeLeftTop : function(left, top){
8040         delete this.lastXY;
8041         this.lastLT = [left, top];
8042     },
8043
8044     // private
8045     beforeFx : function(){
8046         this.beforeAction();
8047         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
8048     },
8049
8050     // private
8051     afterFx : function(){
8052         Roo.Layer.superclass.afterFx.apply(this, arguments);
8053         this.sync(this.isVisible());
8054     },
8055
8056     // private
8057     beforeAction : function(){
8058         if(!this.updating && this.shadow){
8059             this.shadow.hide();
8060         }
8061     },
8062
8063     // overridden Element method
8064     setLeft : function(left){
8065         this.storeLeftTop(left, this.getTop(true));
8066         supr.setLeft.apply(this, arguments);
8067         this.sync();
8068     },
8069
8070     setTop : function(top){
8071         this.storeLeftTop(this.getLeft(true), top);
8072         supr.setTop.apply(this, arguments);
8073         this.sync();
8074     },
8075
8076     setLeftTop : function(left, top){
8077         this.storeLeftTop(left, top);
8078         supr.setLeftTop.apply(this, arguments);
8079         this.sync();
8080     },
8081
8082     setXY : function(xy, a, d, c, e){
8083         this.fixDisplay();
8084         this.beforeAction();
8085         this.storeXY(xy);
8086         var cb = this.createCB(c);
8087         supr.setXY.call(this, xy, a, d, cb, e);
8088         if(!a){
8089             cb();
8090         }
8091     },
8092
8093     // private
8094     createCB : function(c){
8095         var el = this;
8096         return function(){
8097             el.constrainXY();
8098             el.sync(true);
8099             if(c){
8100                 c();
8101             }
8102         };
8103     },
8104
8105     // overridden Element method
8106     setX : function(x, a, d, c, e){
8107         this.setXY([x, this.getY()], a, d, c, e);
8108     },
8109
8110     // overridden Element method
8111     setY : function(y, a, d, c, e){
8112         this.setXY([this.getX(), y], a, d, c, e);
8113     },
8114
8115     // overridden Element method
8116     setSize : function(w, h, a, d, c, e){
8117         this.beforeAction();
8118         var cb = this.createCB(c);
8119         supr.setSize.call(this, w, h, a, d, cb, e);
8120         if(!a){
8121             cb();
8122         }
8123     },
8124
8125     // overridden Element method
8126     setWidth : function(w, a, d, c, e){
8127         this.beforeAction();
8128         var cb = this.createCB(c);
8129         supr.setWidth.call(this, w, a, d, cb, e);
8130         if(!a){
8131             cb();
8132         }
8133     },
8134
8135     // overridden Element method
8136     setHeight : function(h, a, d, c, e){
8137         this.beforeAction();
8138         var cb = this.createCB(c);
8139         supr.setHeight.call(this, h, a, d, cb, e);
8140         if(!a){
8141             cb();
8142         }
8143     },
8144
8145     // overridden Element method
8146     setBounds : function(x, y, w, h, a, d, c, e){
8147         this.beforeAction();
8148         var cb = this.createCB(c);
8149         if(!a){
8150             this.storeXY([x, y]);
8151             supr.setXY.call(this, [x, y]);
8152             supr.setSize.call(this, w, h, a, d, cb, e);
8153             cb();
8154         }else{
8155             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8156         }
8157         return this;
8158     },
8159     
8160     /**
8161      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8162      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8163      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8164      * @param {Number} zindex The new z-index to set
8165      * @return {this} The Layer
8166      */
8167     setZIndex : function(zindex){
8168         this.zindex = zindex;
8169         this.setStyle("z-index", zindex + 2);
8170         if(this.shadow){
8171             this.shadow.setZIndex(zindex + 1);
8172         }
8173         if(this.shim){
8174             this.shim.setStyle("z-index", zindex);
8175         }
8176     }
8177 });
8178 })();/*
8179  * Based on:
8180  * Ext JS Library 1.1.1
8181  * Copyright(c) 2006-2007, Ext JS, LLC.
8182  *
8183  * Originally Released Under LGPL - original licence link has changed is not relivant.
8184  *
8185  * Fork - LGPL
8186  * <script type="text/javascript">
8187  */
8188
8189
8190 /**
8191  * @class Roo.Shadow
8192  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8193  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8194  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8195  * @constructor
8196  * Create a new Shadow
8197  * @param {Object} config The config object
8198  */
8199 Roo.Shadow = function(config){
8200     Roo.apply(this, config);
8201     if(typeof this.mode != "string"){
8202         this.mode = this.defaultMode;
8203     }
8204     var o = this.offset, a = {h: 0};
8205     var rad = Math.floor(this.offset/2);
8206     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8207         case "drop":
8208             a.w = 0;
8209             a.l = a.t = o;
8210             a.t -= 1;
8211             if(Roo.isIE){
8212                 a.l -= this.offset + rad;
8213                 a.t -= this.offset + rad;
8214                 a.w -= rad;
8215                 a.h -= rad;
8216                 a.t += 1;
8217             }
8218         break;
8219         case "sides":
8220             a.w = (o*2);
8221             a.l = -o;
8222             a.t = o-1;
8223             if(Roo.isIE){
8224                 a.l -= (this.offset - rad);
8225                 a.t -= this.offset + rad;
8226                 a.l += 1;
8227                 a.w -= (this.offset - rad)*2;
8228                 a.w -= rad + 1;
8229                 a.h -= 1;
8230             }
8231         break;
8232         case "frame":
8233             a.w = a.h = (o*2);
8234             a.l = a.t = -o;
8235             a.t += 1;
8236             a.h -= 2;
8237             if(Roo.isIE){
8238                 a.l -= (this.offset - rad);
8239                 a.t -= (this.offset - rad);
8240                 a.l += 1;
8241                 a.w -= (this.offset + rad + 1);
8242                 a.h -= (this.offset + rad);
8243                 a.h += 1;
8244             }
8245         break;
8246     };
8247
8248     this.adjusts = a;
8249 };
8250
8251 Roo.Shadow.prototype = {
8252     /**
8253      * @cfg {String} mode
8254      * The shadow display mode.  Supports the following options:<br />
8255      * sides: Shadow displays on both sides and bottom only<br />
8256      * frame: Shadow displays equally on all four sides<br />
8257      * drop: Traditional bottom-right drop shadow (default)
8258      */
8259     /**
8260      * @cfg {String} offset
8261      * The number of pixels to offset the shadow from the element (defaults to 4)
8262      */
8263     offset: 4,
8264
8265     // private
8266     defaultMode: "drop",
8267
8268     /**
8269      * Displays the shadow under the target element
8270      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8271      */
8272     show : function(target){
8273         target = Roo.get(target);
8274         if(!this.el){
8275             this.el = Roo.Shadow.Pool.pull();
8276             if(this.el.dom.nextSibling != target.dom){
8277                 this.el.insertBefore(target);
8278             }
8279         }
8280         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8281         if(Roo.isIE){
8282             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8283         }
8284         this.realign(
8285             target.getLeft(true),
8286             target.getTop(true),
8287             target.getWidth(),
8288             target.getHeight()
8289         );
8290         this.el.dom.style.display = "block";
8291     },
8292
8293     /**
8294      * Returns true if the shadow is visible, else false
8295      */
8296     isVisible : function(){
8297         return this.el ? true : false;  
8298     },
8299
8300     /**
8301      * Direct alignment when values are already available. Show must be called at least once before
8302      * calling this method to ensure it is initialized.
8303      * @param {Number} left The target element left position
8304      * @param {Number} top The target element top position
8305      * @param {Number} width The target element width
8306      * @param {Number} height The target element height
8307      */
8308     realign : function(l, t, w, h){
8309         if(!this.el){
8310             return;
8311         }
8312         var a = this.adjusts, d = this.el.dom, s = d.style;
8313         var iea = 0;
8314         s.left = (l+a.l)+"px";
8315         s.top = (t+a.t)+"px";
8316         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8317  
8318         if(s.width != sws || s.height != shs){
8319             s.width = sws;
8320             s.height = shs;
8321             if(!Roo.isIE){
8322                 var cn = d.childNodes;
8323                 var sww = Math.max(0, (sw-12))+"px";
8324                 cn[0].childNodes[1].style.width = sww;
8325                 cn[1].childNodes[1].style.width = sww;
8326                 cn[2].childNodes[1].style.width = sww;
8327                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8328             }
8329         }
8330     },
8331
8332     /**
8333      * Hides this shadow
8334      */
8335     hide : function(){
8336         if(this.el){
8337             this.el.dom.style.display = "none";
8338             Roo.Shadow.Pool.push(this.el);
8339             delete this.el;
8340         }
8341     },
8342
8343     /**
8344      * Adjust the z-index of this shadow
8345      * @param {Number} zindex The new z-index
8346      */
8347     setZIndex : function(z){
8348         this.zIndex = z;
8349         if(this.el){
8350             this.el.setStyle("z-index", z);
8351         }
8352     }
8353 };
8354
8355 // Private utility class that manages the internal Shadow cache
8356 Roo.Shadow.Pool = function(){
8357     var p = [];
8358     var markup = Roo.isIE ?
8359                  '<div class="x-ie-shadow"></div>' :
8360                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
8361     return {
8362         pull : function(){
8363             var sh = p.shift();
8364             if(!sh){
8365                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8366                 sh.autoBoxAdjust = false;
8367             }
8368             return sh;
8369         },
8370
8371         push : function(sh){
8372             p.push(sh);
8373         }
8374     };
8375 }();/*
8376  * Based on:
8377  * Ext JS Library 1.1.1
8378  * Copyright(c) 2006-2007, Ext JS, LLC.
8379  *
8380  * Originally Released Under LGPL - original licence link has changed is not relivant.
8381  *
8382  * Fork - LGPL
8383  * <script type="text/javascript">
8384  */
8385
8386 /**
8387  * @class Roo.BoxComponent
8388  * @extends Roo.Component
8389  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8390  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8391  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8392  * layout containers.
8393  * @constructor
8394  * @param {Roo.Element/String/Object} config The configuration options.
8395  */
8396 Roo.BoxComponent = function(config){
8397     Roo.Component.call(this, config);
8398     this.addEvents({
8399         /**
8400          * @event resize
8401          * Fires after the component is resized.
8402              * @param {Roo.Component} this
8403              * @param {Number} adjWidth The box-adjusted width that was set
8404              * @param {Number} adjHeight The box-adjusted height that was set
8405              * @param {Number} rawWidth The width that was originally specified
8406              * @param {Number} rawHeight The height that was originally specified
8407              */
8408         resize : true,
8409         /**
8410          * @event move
8411          * Fires after the component is moved.
8412              * @param {Roo.Component} this
8413              * @param {Number} x The new x position
8414              * @param {Number} y The new y position
8415              */
8416         move : true
8417     });
8418 };
8419
8420 Roo.extend(Roo.BoxComponent, Roo.Component, {
8421     // private, set in afterRender to signify that the component has been rendered
8422     boxReady : false,
8423     // private, used to defer height settings to subclasses
8424     deferHeight: false,
8425     /** @cfg {Number} width
8426      * width (optional) size of component
8427      */
8428      /** @cfg {Number} height
8429      * height (optional) size of component
8430      */
8431      
8432     /**
8433      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8434      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8435      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8436      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8437      * @return {Roo.BoxComponent} this
8438      */
8439     setSize : function(w, h){
8440         // support for standard size objects
8441         if(typeof w == 'object'){
8442             h = w.height;
8443             w = w.width;
8444         }
8445         // not rendered
8446         if(!this.boxReady){
8447             this.width = w;
8448             this.height = h;
8449             return this;
8450         }
8451
8452         // prevent recalcs when not needed
8453         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8454             return this;
8455         }
8456         this.lastSize = {width: w, height: h};
8457
8458         var adj = this.adjustSize(w, h);
8459         var aw = adj.width, ah = adj.height;
8460         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8461             var rz = this.getResizeEl();
8462             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8463                 rz.setSize(aw, ah);
8464             }else if(!this.deferHeight && ah !== undefined){
8465                 rz.setHeight(ah);
8466             }else if(aw !== undefined){
8467                 rz.setWidth(aw);
8468             }
8469             this.onResize(aw, ah, w, h);
8470             this.fireEvent('resize', this, aw, ah, w, h);
8471         }
8472         return this;
8473     },
8474
8475     /**
8476      * Gets the current size of the component's underlying element.
8477      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8478      */
8479     getSize : function(){
8480         return this.el.getSize();
8481     },
8482
8483     /**
8484      * Gets the current XY position of the component's underlying element.
8485      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8486      * @return {Array} The XY position of the element (e.g., [100, 200])
8487      */
8488     getPosition : function(local){
8489         if(local === true){
8490             return [this.el.getLeft(true), this.el.getTop(true)];
8491         }
8492         return this.xy || this.el.getXY();
8493     },
8494
8495     /**
8496      * Gets the current box measurements of the component's underlying element.
8497      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8498      * @returns {Object} box An object in the format {x, y, width, height}
8499      */
8500     getBox : function(local){
8501         var s = this.el.getSize();
8502         if(local){
8503             s.x = this.el.getLeft(true);
8504             s.y = this.el.getTop(true);
8505         }else{
8506             var xy = this.xy || this.el.getXY();
8507             s.x = xy[0];
8508             s.y = xy[1];
8509         }
8510         return s;
8511     },
8512
8513     /**
8514      * Sets the current box measurements of the component's underlying element.
8515      * @param {Object} box An object in the format {x, y, width, height}
8516      * @returns {Roo.BoxComponent} this
8517      */
8518     updateBox : function(box){
8519         this.setSize(box.width, box.height);
8520         this.setPagePosition(box.x, box.y);
8521         return this;
8522     },
8523
8524     // protected
8525     getResizeEl : function(){
8526         return this.resizeEl || this.el;
8527     },
8528
8529     // protected
8530     getPositionEl : function(){
8531         return this.positionEl || this.el;
8532     },
8533
8534     /**
8535      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8536      * This method fires the move event.
8537      * @param {Number} left The new left
8538      * @param {Number} top The new top
8539      * @returns {Roo.BoxComponent} this
8540      */
8541     setPosition : function(x, y){
8542         this.x = x;
8543         this.y = y;
8544         if(!this.boxReady){
8545             return this;
8546         }
8547         var adj = this.adjustPosition(x, y);
8548         var ax = adj.x, ay = adj.y;
8549
8550         var el = this.getPositionEl();
8551         if(ax !== undefined || ay !== undefined){
8552             if(ax !== undefined && ay !== undefined){
8553                 el.setLeftTop(ax, ay);
8554             }else if(ax !== undefined){
8555                 el.setLeft(ax);
8556             }else if(ay !== undefined){
8557                 el.setTop(ay);
8558             }
8559             this.onPosition(ax, ay);
8560             this.fireEvent('move', this, ax, ay);
8561         }
8562         return this;
8563     },
8564
8565     /**
8566      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8567      * This method fires the move event.
8568      * @param {Number} x The new x position
8569      * @param {Number} y The new y position
8570      * @returns {Roo.BoxComponent} this
8571      */
8572     setPagePosition : function(x, y){
8573         this.pageX = x;
8574         this.pageY = y;
8575         if(!this.boxReady){
8576             return;
8577         }
8578         if(x === undefined || y === undefined){ // cannot translate undefined points
8579             return;
8580         }
8581         var p = this.el.translatePoints(x, y);
8582         this.setPosition(p.left, p.top);
8583         return this;
8584     },
8585
8586     // private
8587     onRender : function(ct, position){
8588         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8589         if(this.resizeEl){
8590             this.resizeEl = Roo.get(this.resizeEl);
8591         }
8592         if(this.positionEl){
8593             this.positionEl = Roo.get(this.positionEl);
8594         }
8595     },
8596
8597     // private
8598     afterRender : function(){
8599         Roo.BoxComponent.superclass.afterRender.call(this);
8600         this.boxReady = true;
8601         this.setSize(this.width, this.height);
8602         if(this.x || this.y){
8603             this.setPosition(this.x, this.y);
8604         }
8605         if(this.pageX || this.pageY){
8606             this.setPagePosition(this.pageX, this.pageY);
8607         }
8608     },
8609
8610     /**
8611      * Force the component's size to recalculate based on the underlying element's current height and width.
8612      * @returns {Roo.BoxComponent} this
8613      */
8614     syncSize : function(){
8615         delete this.lastSize;
8616         this.setSize(this.el.getWidth(), this.el.getHeight());
8617         return this;
8618     },
8619
8620     /**
8621      * Called after the component is resized, this method is empty by default but can be implemented by any
8622      * subclass that needs to perform custom logic after a resize occurs.
8623      * @param {Number} adjWidth The box-adjusted width that was set
8624      * @param {Number} adjHeight The box-adjusted height that was set
8625      * @param {Number} rawWidth The width that was originally specified
8626      * @param {Number} rawHeight The height that was originally specified
8627      */
8628     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8629
8630     },
8631
8632     /**
8633      * Called after the component is moved, this method is empty by default but can be implemented by any
8634      * subclass that needs to perform custom logic after a move occurs.
8635      * @param {Number} x The new x position
8636      * @param {Number} y The new y position
8637      */
8638     onPosition : function(x, y){
8639
8640     },
8641
8642     // private
8643     adjustSize : function(w, h){
8644         if(this.autoWidth){
8645             w = 'auto';
8646         }
8647         if(this.autoHeight){
8648             h = 'auto';
8649         }
8650         return {width : w, height: h};
8651     },
8652
8653     // private
8654     adjustPosition : function(x, y){
8655         return {x : x, y: y};
8656     }
8657 });/*
8658  * Based on:
8659  * Ext JS Library 1.1.1
8660  * Copyright(c) 2006-2007, Ext JS, LLC.
8661  *
8662  * Originally Released Under LGPL - original licence link has changed is not relivant.
8663  *
8664  * Fork - LGPL
8665  * <script type="text/javascript">
8666  */
8667
8668
8669 /**
8670  * @class Roo.SplitBar
8671  * @extends Roo.util.Observable
8672  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8673  * <br><br>
8674  * Usage:
8675  * <pre><code>
8676 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8677                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8678 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8679 split.minSize = 100;
8680 split.maxSize = 600;
8681 split.animate = true;
8682 split.on('moved', splitterMoved);
8683 </code></pre>
8684  * @constructor
8685  * Create a new SplitBar
8686  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8687  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8688  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8689  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8690                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8691                         position of the SplitBar).
8692  */
8693 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8694     
8695     /** @private */
8696     this.el = Roo.get(dragElement, true);
8697     this.el.dom.unselectable = "on";
8698     /** @private */
8699     this.resizingEl = Roo.get(resizingElement, true);
8700
8701     /**
8702      * @private
8703      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8704      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8705      * @type Number
8706      */
8707     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8708     
8709     /**
8710      * The minimum size of the resizing element. (Defaults to 0)
8711      * @type Number
8712      */
8713     this.minSize = 0;
8714     
8715     /**
8716      * The maximum size of the resizing element. (Defaults to 2000)
8717      * @type Number
8718      */
8719     this.maxSize = 2000;
8720     
8721     /**
8722      * Whether to animate the transition to the new size
8723      * @type Boolean
8724      */
8725     this.animate = false;
8726     
8727     /**
8728      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8729      * @type Boolean
8730      */
8731     this.useShim = false;
8732     
8733     /** @private */
8734     this.shim = null;
8735     
8736     if(!existingProxy){
8737         /** @private */
8738         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8739     }else{
8740         this.proxy = Roo.get(existingProxy).dom;
8741     }
8742     /** @private */
8743     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8744     
8745     /** @private */
8746     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8747     
8748     /** @private */
8749     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8750     
8751     /** @private */
8752     this.dragSpecs = {};
8753     
8754     /**
8755      * @private The adapter to use to positon and resize elements
8756      */
8757     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8758     this.adapter.init(this);
8759     
8760     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8761         /** @private */
8762         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8763         this.el.addClass("x-splitbar-h");
8764     }else{
8765         /** @private */
8766         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8767         this.el.addClass("x-splitbar-v");
8768     }
8769     
8770     this.addEvents({
8771         /**
8772          * @event resize
8773          * Fires when the splitter is moved (alias for {@link #event-moved})
8774          * @param {Roo.SplitBar} this
8775          * @param {Number} newSize the new width or height
8776          */
8777         "resize" : true,
8778         /**
8779          * @event moved
8780          * Fires when the splitter is moved
8781          * @param {Roo.SplitBar} this
8782          * @param {Number} newSize the new width or height
8783          */
8784         "moved" : true,
8785         /**
8786          * @event beforeresize
8787          * Fires before the splitter is dragged
8788          * @param {Roo.SplitBar} this
8789          */
8790         "beforeresize" : true,
8791
8792         "beforeapply" : true
8793     });
8794
8795     Roo.util.Observable.call(this);
8796 };
8797
8798 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8799     onStartProxyDrag : function(x, y){
8800         this.fireEvent("beforeresize", this);
8801         if(!this.overlay){
8802             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8803             o.unselectable();
8804             o.enableDisplayMode("block");
8805             // all splitbars share the same overlay
8806             Roo.SplitBar.prototype.overlay = o;
8807         }
8808         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8809         this.overlay.show();
8810         Roo.get(this.proxy).setDisplayed("block");
8811         var size = this.adapter.getElementSize(this);
8812         this.activeMinSize = this.getMinimumSize();;
8813         this.activeMaxSize = this.getMaximumSize();;
8814         var c1 = size - this.activeMinSize;
8815         var c2 = Math.max(this.activeMaxSize - size, 0);
8816         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8817             this.dd.resetConstraints();
8818             this.dd.setXConstraint(
8819                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8820                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8821             );
8822             this.dd.setYConstraint(0, 0);
8823         }else{
8824             this.dd.resetConstraints();
8825             this.dd.setXConstraint(0, 0);
8826             this.dd.setYConstraint(
8827                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8828                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8829             );
8830          }
8831         this.dragSpecs.startSize = size;
8832         this.dragSpecs.startPoint = [x, y];
8833         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8834     },
8835     
8836     /** 
8837      * @private Called after the drag operation by the DDProxy
8838      */
8839     onEndProxyDrag : function(e){
8840         Roo.get(this.proxy).setDisplayed(false);
8841         var endPoint = Roo.lib.Event.getXY(e);
8842         if(this.overlay){
8843             this.overlay.hide();
8844         }
8845         var newSize;
8846         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8847             newSize = this.dragSpecs.startSize + 
8848                 (this.placement == Roo.SplitBar.LEFT ?
8849                     endPoint[0] - this.dragSpecs.startPoint[0] :
8850                     this.dragSpecs.startPoint[0] - endPoint[0]
8851                 );
8852         }else{
8853             newSize = this.dragSpecs.startSize + 
8854                 (this.placement == Roo.SplitBar.TOP ?
8855                     endPoint[1] - this.dragSpecs.startPoint[1] :
8856                     this.dragSpecs.startPoint[1] - endPoint[1]
8857                 );
8858         }
8859         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8860         if(newSize != this.dragSpecs.startSize){
8861             if(this.fireEvent('beforeapply', this, newSize) !== false){
8862                 this.adapter.setElementSize(this, newSize);
8863                 this.fireEvent("moved", this, newSize);
8864                 this.fireEvent("resize", this, newSize);
8865             }
8866         }
8867     },
8868     
8869     /**
8870      * Get the adapter this SplitBar uses
8871      * @return The adapter object
8872      */
8873     getAdapter : function(){
8874         return this.adapter;
8875     },
8876     
8877     /**
8878      * Set the adapter this SplitBar uses
8879      * @param {Object} adapter A SplitBar adapter object
8880      */
8881     setAdapter : function(adapter){
8882         this.adapter = adapter;
8883         this.adapter.init(this);
8884     },
8885     
8886     /**
8887      * Gets the minimum size for the resizing element
8888      * @return {Number} The minimum size
8889      */
8890     getMinimumSize : function(){
8891         return this.minSize;
8892     },
8893     
8894     /**
8895      * Sets the minimum size for the resizing element
8896      * @param {Number} minSize The minimum size
8897      */
8898     setMinimumSize : function(minSize){
8899         this.minSize = minSize;
8900     },
8901     
8902     /**
8903      * Gets the maximum size for the resizing element
8904      * @return {Number} The maximum size
8905      */
8906     getMaximumSize : function(){
8907         return this.maxSize;
8908     },
8909     
8910     /**
8911      * Sets the maximum size for the resizing element
8912      * @param {Number} maxSize The maximum size
8913      */
8914     setMaximumSize : function(maxSize){
8915         this.maxSize = maxSize;
8916     },
8917     
8918     /**
8919      * Sets the initialize size for the resizing element
8920      * @param {Number} size The initial size
8921      */
8922     setCurrentSize : function(size){
8923         var oldAnimate = this.animate;
8924         this.animate = false;
8925         this.adapter.setElementSize(this, size);
8926         this.animate = oldAnimate;
8927     },
8928     
8929     /**
8930      * Destroy this splitbar. 
8931      * @param {Boolean} removeEl True to remove the element
8932      */
8933     destroy : function(removeEl){
8934         if(this.shim){
8935             this.shim.remove();
8936         }
8937         this.dd.unreg();
8938         this.proxy.parentNode.removeChild(this.proxy);
8939         if(removeEl){
8940             this.el.remove();
8941         }
8942     }
8943 });
8944
8945 /**
8946  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
8947  */
8948 Roo.SplitBar.createProxy = function(dir){
8949     var proxy = new Roo.Element(document.createElement("div"));
8950     proxy.unselectable();
8951     var cls = 'x-splitbar-proxy';
8952     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8953     document.body.appendChild(proxy.dom);
8954     return proxy.dom;
8955 };
8956
8957 /** 
8958  * @class Roo.SplitBar.BasicLayoutAdapter
8959  * Default Adapter. It assumes the splitter and resizing element are not positioned
8960  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8961  */
8962 Roo.SplitBar.BasicLayoutAdapter = function(){
8963 };
8964
8965 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8966     // do nothing for now
8967     init : function(s){
8968     
8969     },
8970     /**
8971      * Called before drag operations to get the current size of the resizing element. 
8972      * @param {Roo.SplitBar} s The SplitBar using this adapter
8973      */
8974      getElementSize : function(s){
8975         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8976             return s.resizingEl.getWidth();
8977         }else{
8978             return s.resizingEl.getHeight();
8979         }
8980     },
8981     
8982     /**
8983      * Called after drag operations to set the size of the resizing element.
8984      * @param {Roo.SplitBar} s The SplitBar using this adapter
8985      * @param {Number} newSize The new size to set
8986      * @param {Function} onComplete A function to be invoked when resizing is complete
8987      */
8988     setElementSize : function(s, newSize, onComplete){
8989         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8990             if(!s.animate){
8991                 s.resizingEl.setWidth(newSize);
8992                 if(onComplete){
8993                     onComplete(s, newSize);
8994                 }
8995             }else{
8996                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8997             }
8998         }else{
8999             
9000             if(!s.animate){
9001                 s.resizingEl.setHeight(newSize);
9002                 if(onComplete){
9003                     onComplete(s, newSize);
9004                 }
9005             }else{
9006                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
9007             }
9008         }
9009     }
9010 };
9011
9012 /** 
9013  *@class Roo.SplitBar.AbsoluteLayoutAdapter
9014  * @extends Roo.SplitBar.BasicLayoutAdapter
9015  * Adapter that  moves the splitter element to align with the resized sizing element. 
9016  * Used with an absolute positioned SplitBar.
9017  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
9018  * document.body, make sure you assign an id to the body element.
9019  */
9020 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
9021     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
9022     this.container = Roo.get(container);
9023 };
9024
9025 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
9026     init : function(s){
9027         this.basic.init(s);
9028     },
9029     
9030     getElementSize : function(s){
9031         return this.basic.getElementSize(s);
9032     },
9033     
9034     setElementSize : function(s, newSize, onComplete){
9035         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
9036     },
9037     
9038     moveSplitter : function(s){
9039         var yes = Roo.SplitBar;
9040         switch(s.placement){
9041             case yes.LEFT:
9042                 s.el.setX(s.resizingEl.getRight());
9043                 break;
9044             case yes.RIGHT:
9045                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
9046                 break;
9047             case yes.TOP:
9048                 s.el.setY(s.resizingEl.getBottom());
9049                 break;
9050             case yes.BOTTOM:
9051                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
9052                 break;
9053         }
9054     }
9055 };
9056
9057 /**
9058  * Orientation constant - Create a vertical SplitBar
9059  * @static
9060  * @type Number
9061  */
9062 Roo.SplitBar.VERTICAL = 1;
9063
9064 /**
9065  * Orientation constant - Create a horizontal SplitBar
9066  * @static
9067  * @type Number
9068  */
9069 Roo.SplitBar.HORIZONTAL = 2;
9070
9071 /**
9072  * Placement constant - The resizing element is to the left of the splitter element
9073  * @static
9074  * @type Number
9075  */
9076 Roo.SplitBar.LEFT = 1;
9077
9078 /**
9079  * Placement constant - The resizing element is to the right of the splitter element
9080  * @static
9081  * @type Number
9082  */
9083 Roo.SplitBar.RIGHT = 2;
9084
9085 /**
9086  * Placement constant - The resizing element is positioned above the splitter element
9087  * @static
9088  * @type Number
9089  */
9090 Roo.SplitBar.TOP = 3;
9091
9092 /**
9093  * Placement constant - The resizing element is positioned under splitter element
9094  * @static
9095  * @type Number
9096  */
9097 Roo.SplitBar.BOTTOM = 4;
9098 /*
9099  * Based on:
9100  * Ext JS Library 1.1.1
9101  * Copyright(c) 2006-2007, Ext JS, LLC.
9102  *
9103  * Originally Released Under LGPL - original licence link has changed is not relivant.
9104  *
9105  * Fork - LGPL
9106  * <script type="text/javascript">
9107  */
9108
9109 /**
9110  * @class Roo.View
9111  * @extends Roo.util.Observable
9112  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9113  * This class also supports single and multi selection modes. <br>
9114  * Create a data model bound view:
9115  <pre><code>
9116  var store = new Roo.data.Store(...);
9117
9118  var view = new Roo.View({
9119     el : "my-element",
9120     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9121  
9122     singleSelect: true,
9123     selectedClass: "ydataview-selected",
9124     store: store
9125  });
9126
9127  // listen for node click?
9128  view.on("click", function(vw, index, node, e){
9129  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9130  });
9131
9132  // load XML data
9133  dataModel.load("foobar.xml");
9134  </code></pre>
9135  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9136  * <br><br>
9137  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9138  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9139  * 
9140  * Note: old style constructor is still suported (container, template, config)
9141  * 
9142  * @constructor
9143  * Create a new View
9144  * @param {Object} config The config object
9145  * 
9146  */
9147 Roo.View = function(config, depreciated_tpl, depreciated_config){
9148     
9149     if (typeof(depreciated_tpl) == 'undefined') {
9150         // new way.. - universal constructor.
9151         Roo.apply(this, config);
9152         this.el  = Roo.get(this.el);
9153     } else {
9154         // old format..
9155         this.el  = Roo.get(config);
9156         this.tpl = depreciated_tpl;
9157         Roo.apply(this, depreciated_config);
9158     }
9159      
9160     
9161     if(typeof(this.tpl) == "string"){
9162         this.tpl = new Roo.Template(this.tpl);
9163     } else {
9164         // support xtype ctors..
9165         this.tpl = new Roo.factory(this.tpl, Roo);
9166     }
9167     
9168     
9169     this.tpl.compile();
9170    
9171
9172      
9173     /** @private */
9174     this.addEvents({
9175         /**
9176          * @event beforeclick
9177          * Fires before a click is processed. Returns false to cancel the default action.
9178          * @param {Roo.View} this
9179          * @param {Number} index The index of the target node
9180          * @param {HTMLElement} node The target node
9181          * @param {Roo.EventObject} e The raw event object
9182          */
9183             "beforeclick" : true,
9184         /**
9185          * @event click
9186          * Fires when a template node is clicked.
9187          * @param {Roo.View} this
9188          * @param {Number} index The index of the target node
9189          * @param {HTMLElement} node The target node
9190          * @param {Roo.EventObject} e The raw event object
9191          */
9192             "click" : true,
9193         /**
9194          * @event dblclick
9195          * Fires when a template node is double clicked.
9196          * @param {Roo.View} this
9197          * @param {Number} index The index of the target node
9198          * @param {HTMLElement} node The target node
9199          * @param {Roo.EventObject} e The raw event object
9200          */
9201             "dblclick" : true,
9202         /**
9203          * @event contextmenu
9204          * Fires when a template node is right clicked.
9205          * @param {Roo.View} this
9206          * @param {Number} index The index of the target node
9207          * @param {HTMLElement} node The target node
9208          * @param {Roo.EventObject} e The raw event object
9209          */
9210             "contextmenu" : true,
9211         /**
9212          * @event selectionchange
9213          * Fires when the selected nodes change.
9214          * @param {Roo.View} this
9215          * @param {Array} selections Array of the selected nodes
9216          */
9217             "selectionchange" : true,
9218     
9219         /**
9220          * @event beforeselect
9221          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9222          * @param {Roo.View} this
9223          * @param {HTMLElement} node The node to be selected
9224          * @param {Array} selections Array of currently selected nodes
9225          */
9226             "beforeselect" : true,
9227         /**
9228          * @event preparedata
9229          * Fires on every row to render, to allow you to change the data.
9230          * @param {Roo.View} this
9231          * @param {Object} data to be rendered (change this)
9232          */
9233           "preparedata" : true
9234         });
9235
9236     this.el.on({
9237         "click": this.onClick,
9238         "dblclick": this.onDblClick,
9239         "contextmenu": this.onContextMenu,
9240         scope:this
9241     });
9242
9243     this.selections = [];
9244     this.nodes = [];
9245     this.cmp = new Roo.CompositeElementLite([]);
9246     if(this.store){
9247         this.store = Roo.factory(this.store, Roo.data);
9248         this.setStore(this.store, true);
9249     }
9250     Roo.View.superclass.constructor.call(this);
9251 };
9252
9253 Roo.extend(Roo.View, Roo.util.Observable, {
9254     
9255      /**
9256      * @cfg {Roo.data.Store} store Data store to load data from.
9257      */
9258     store : false,
9259     
9260     /**
9261      * @cfg {String|Roo.Element} el The container element.
9262      */
9263     el : '',
9264     
9265     /**
9266      * @cfg {String|Roo.Template} tpl The template used by this View 
9267      */
9268     tpl : false,
9269     /**
9270      * @cfg {String} dataName the named area of the template to use as the data area
9271      *                          Works with domtemplates roo-name="name"
9272      */
9273     dataName: false,
9274     /**
9275      * @cfg {String} selectedClass The css class to add to selected nodes
9276      */
9277     selectedClass : "x-view-selected",
9278      /**
9279      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9280      */
9281     emptyText : "",
9282     /**
9283      * @cfg {Boolean} multiSelect Allow multiple selection
9284      */
9285     multiSelect : false,
9286     /**
9287      * @cfg {Boolean} singleSelect Allow single selection
9288      */
9289     singleSelect:  false,
9290     
9291     /**
9292      * @cfg {Boolean} toggleSelect - selecting 
9293      */
9294     toggleSelect : false,
9295     
9296     /**
9297      * Returns the element this view is bound to.
9298      * @return {Roo.Element}
9299      */
9300     getEl : function(){
9301         return this.el;
9302     },
9303
9304     /**
9305      * Refreshes the view.
9306      */
9307     refresh : function(){
9308         var t = this.tpl;
9309         
9310         // if we are using something like 'domtemplate', then
9311         // the what gets used is:
9312         // t.applySubtemplate(NAME, data, wrapping data..)
9313         // the outer template then get' applied with
9314         //     the store 'extra data'
9315         // and the body get's added to the
9316         //      roo-name="data" node?
9317         //      <span class='roo-tpl-{name}'></span> ?????
9318         
9319         
9320         
9321         this.clearSelections();
9322         this.el.update("");
9323         var html = [];
9324         var records = this.store.getRange();
9325         if(records.length < 1) {
9326             
9327             // is this valid??  = should it render a template??
9328             
9329             this.el.update(this.emptyText);
9330             return;
9331         }
9332         var el = this.el;
9333         if (this.dataName) {
9334             this.el.update(t.apply(this.store.meta)); //????
9335             el = this.el.child('.roo-tpl-' + this.dataName);
9336         }
9337         
9338         for(var i = 0, len = records.length; i < len; i++){
9339             var data = this.prepareData(records[i].data, i, records[i]);
9340             this.fireEvent("preparedata", this, data, i, records[i]);
9341             html[html.length] = Roo.util.Format.trim(
9342                 this.dataName ?
9343                     t.applySubtemplate(this.dataName, data, this.store.meta) :
9344                     t.apply(data)
9345             );
9346         }
9347         
9348         
9349         
9350         el.update(html.join(""));
9351         this.nodes = el.dom.childNodes;
9352         this.updateIndexes(0);
9353     },
9354
9355     /**
9356      * Function to override to reformat the data that is sent to
9357      * the template for each node.
9358      * DEPRICATED - use the preparedata event handler.
9359      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9360      * a JSON object for an UpdateManager bound view).
9361      */
9362     prepareData : function(data, index, record)
9363     {
9364         this.fireEvent("preparedata", this, data, index, record);
9365         return data;
9366     },
9367
9368     onUpdate : function(ds, record){
9369         this.clearSelections();
9370         var index = this.store.indexOf(record);
9371         var n = this.nodes[index];
9372         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9373         n.parentNode.removeChild(n);
9374         this.updateIndexes(index, index);
9375     },
9376
9377     
9378     
9379 // --------- FIXME     
9380     onAdd : function(ds, records, index)
9381     {
9382         this.clearSelections();
9383         if(this.nodes.length == 0){
9384             this.refresh();
9385             return;
9386         }
9387         var n = this.nodes[index];
9388         for(var i = 0, len = records.length; i < len; i++){
9389             var d = this.prepareData(records[i].data, i, records[i]);
9390             if(n){
9391                 this.tpl.insertBefore(n, d);
9392             }else{
9393                 
9394                 this.tpl.append(this.el, d);
9395             }
9396         }
9397         this.updateIndexes(index);
9398     },
9399
9400     onRemove : function(ds, record, index){
9401         this.clearSelections();
9402         var el = this.dataName  ?
9403             this.el.child('.roo-tpl-' + this.dataName) :
9404             this.el; 
9405         el.dom.removeChild(this.nodes[index]);
9406         this.updateIndexes(index);
9407     },
9408
9409     /**
9410      * Refresh an individual node.
9411      * @param {Number} index
9412      */
9413     refreshNode : function(index){
9414         this.onUpdate(this.store, this.store.getAt(index));
9415     },
9416
9417     updateIndexes : function(startIndex, endIndex){
9418         var ns = this.nodes;
9419         startIndex = startIndex || 0;
9420         endIndex = endIndex || ns.length - 1;
9421         for(var i = startIndex; i <= endIndex; i++){
9422             ns[i].nodeIndex = i;
9423         }
9424     },
9425
9426     /**
9427      * Changes the data store this view uses and refresh the view.
9428      * @param {Store} store
9429      */
9430     setStore : function(store, initial){
9431         if(!initial && this.store){
9432             this.store.un("datachanged", this.refresh);
9433             this.store.un("add", this.onAdd);
9434             this.store.un("remove", this.onRemove);
9435             this.store.un("update", this.onUpdate);
9436             this.store.un("clear", this.refresh);
9437         }
9438         if(store){
9439           
9440             store.on("datachanged", this.refresh, this);
9441             store.on("add", this.onAdd, this);
9442             store.on("remove", this.onRemove, this);
9443             store.on("update", this.onUpdate, this);
9444             store.on("clear", this.refresh, this);
9445         }
9446         
9447         if(store){
9448             this.refresh();
9449         }
9450     },
9451
9452     /**
9453      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9454      * @param {HTMLElement} node
9455      * @return {HTMLElement} The template node
9456      */
9457     findItemFromChild : function(node){
9458         var el = this.dataName  ?
9459             this.el.child('.roo-tpl-' + this.dataName,true) :
9460             this.el.dom; 
9461         
9462         if(!node || node.parentNode == el){
9463                     return node;
9464             }
9465             var p = node.parentNode;
9466             while(p && p != el){
9467             if(p.parentNode == el){
9468                 return p;
9469             }
9470             p = p.parentNode;
9471         }
9472             return null;
9473     },
9474
9475     /** @ignore */
9476     onClick : function(e){
9477         var item = this.findItemFromChild(e.getTarget());
9478         if(item){
9479             var index = this.indexOf(item);
9480             if(this.onItemClick(item, index, e) !== false){
9481                 this.fireEvent("click", this, index, item, e);
9482             }
9483         }else{
9484             this.clearSelections();
9485         }
9486     },
9487
9488     /** @ignore */
9489     onContextMenu : function(e){
9490         var item = this.findItemFromChild(e.getTarget());
9491         if(item){
9492             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9493         }
9494     },
9495
9496     /** @ignore */
9497     onDblClick : function(e){
9498         var item = this.findItemFromChild(e.getTarget());
9499         if(item){
9500             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9501         }
9502     },
9503
9504     onItemClick : function(item, index, e)
9505     {
9506         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9507             return false;
9508         }
9509         if (this.toggleSelect) {
9510             var m = this.isSelected(item) ? 'unselect' : 'select';
9511             Roo.log(m);
9512             var _t = this;
9513             _t[m](item, true, false);
9514             return true;
9515         }
9516         if(this.multiSelect || this.singleSelect){
9517             if(this.multiSelect && e.shiftKey && this.lastSelection){
9518                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9519             }else{
9520                 this.select(item, this.multiSelect && e.ctrlKey);
9521                 this.lastSelection = item;
9522             }
9523             e.preventDefault();
9524         }
9525         return true;
9526     },
9527
9528     /**
9529      * Get the number of selected nodes.
9530      * @return {Number}
9531      */
9532     getSelectionCount : function(){
9533         return this.selections.length;
9534     },
9535
9536     /**
9537      * Get the currently selected nodes.
9538      * @return {Array} An array of HTMLElements
9539      */
9540     getSelectedNodes : function(){
9541         return this.selections;
9542     },
9543
9544     /**
9545      * Get the indexes of the selected nodes.
9546      * @return {Array}
9547      */
9548     getSelectedIndexes : function(){
9549         var indexes = [], s = this.selections;
9550         for(var i = 0, len = s.length; i < len; i++){
9551             indexes.push(s[i].nodeIndex);
9552         }
9553         return indexes;
9554     },
9555
9556     /**
9557      * Clear all selections
9558      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9559      */
9560     clearSelections : function(suppressEvent){
9561         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9562             this.cmp.elements = this.selections;
9563             this.cmp.removeClass(this.selectedClass);
9564             this.selections = [];
9565             if(!suppressEvent){
9566                 this.fireEvent("selectionchange", this, this.selections);
9567             }
9568         }
9569     },
9570
9571     /**
9572      * Returns true if the passed node is selected
9573      * @param {HTMLElement/Number} node The node or node index
9574      * @return {Boolean}
9575      */
9576     isSelected : function(node){
9577         var s = this.selections;
9578         if(s.length < 1){
9579             return false;
9580         }
9581         node = this.getNode(node);
9582         return s.indexOf(node) !== -1;
9583     },
9584
9585     /**
9586      * Selects nodes.
9587      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
9588      * @param {Boolean} keepExisting (optional) true to keep existing selections
9589      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9590      */
9591     select : function(nodeInfo, keepExisting, suppressEvent){
9592         if(nodeInfo instanceof Array){
9593             if(!keepExisting){
9594                 this.clearSelections(true);
9595             }
9596             for(var i = 0, len = nodeInfo.length; i < len; i++){
9597                 this.select(nodeInfo[i], true, true);
9598             }
9599             return;
9600         } 
9601         var node = this.getNode(nodeInfo);
9602         if(!node || this.isSelected(node)){
9603             return; // already selected.
9604         }
9605         if(!keepExisting){
9606             this.clearSelections(true);
9607         }
9608         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9609             Roo.fly(node).addClass(this.selectedClass);
9610             this.selections.push(node);
9611             if(!suppressEvent){
9612                 this.fireEvent("selectionchange", this, this.selections);
9613             }
9614         }
9615         
9616         
9617     },
9618       /**
9619      * Unselects nodes.
9620      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
9621      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9622      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9623      */
9624     unselect : function(nodeInfo, keepExisting, suppressEvent)
9625     {
9626         if(nodeInfo instanceof Array){
9627             Roo.each(this.selections, function(s) {
9628                 this.unselect(s, nodeInfo);
9629             }, this);
9630             return;
9631         }
9632         var node = this.getNode(nodeInfo);
9633         if(!node || !this.isSelected(node)){
9634             Roo.log("not selected");
9635             return; // not selected.
9636         }
9637         // fireevent???
9638         var ns = [];
9639         Roo.each(this.selections, function(s) {
9640             if (s == node ) {
9641                 Roo.fly(node).removeClass(this.selectedClass);
9642
9643                 return;
9644             }
9645             ns.push(s);
9646         },this);
9647         
9648         this.selections= ns;
9649         this.fireEvent("selectionchange", this, this.selections);
9650     },
9651
9652     /**
9653      * Gets a template node.
9654      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9655      * @return {HTMLElement} The node or null if it wasn't found
9656      */
9657     getNode : function(nodeInfo){
9658         if(typeof nodeInfo == "string"){
9659             return document.getElementById(nodeInfo);
9660         }else if(typeof nodeInfo == "number"){
9661             return this.nodes[nodeInfo];
9662         }
9663         return nodeInfo;
9664     },
9665
9666     /**
9667      * Gets a range template nodes.
9668      * @param {Number} startIndex
9669      * @param {Number} endIndex
9670      * @return {Array} An array of nodes
9671      */
9672     getNodes : function(start, end){
9673         var ns = this.nodes;
9674         start = start || 0;
9675         end = typeof end == "undefined" ? ns.length - 1 : end;
9676         var nodes = [];
9677         if(start <= end){
9678             for(var i = start; i <= end; i++){
9679                 nodes.push(ns[i]);
9680             }
9681         } else{
9682             for(var i = start; i >= end; i--){
9683                 nodes.push(ns[i]);
9684             }
9685         }
9686         return nodes;
9687     },
9688
9689     /**
9690      * Finds the index of the passed node
9691      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9692      * @return {Number} The index of the node or -1
9693      */
9694     indexOf : function(node){
9695         node = this.getNode(node);
9696         if(typeof node.nodeIndex == "number"){
9697             return node.nodeIndex;
9698         }
9699         var ns = this.nodes;
9700         for(var i = 0, len = ns.length; i < len; i++){
9701             if(ns[i] == node){
9702                 return i;
9703             }
9704         }
9705         return -1;
9706     }
9707 });
9708 /*
9709  * Based on:
9710  * Ext JS Library 1.1.1
9711  * Copyright(c) 2006-2007, Ext JS, LLC.
9712  *
9713  * Originally Released Under LGPL - original licence link has changed is not relivant.
9714  *
9715  * Fork - LGPL
9716  * <script type="text/javascript">
9717  */
9718
9719 /**
9720  * @class Roo.JsonView
9721  * @extends Roo.View
9722  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9723 <pre><code>
9724 var view = new Roo.JsonView({
9725     container: "my-element",
9726     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9727     multiSelect: true, 
9728     jsonRoot: "data" 
9729 });
9730
9731 // listen for node click?
9732 view.on("click", function(vw, index, node, e){
9733     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9734 });
9735
9736 // direct load of JSON data
9737 view.load("foobar.php");
9738
9739 // Example from my blog list
9740 var tpl = new Roo.Template(
9741     '&lt;div class="entry"&gt;' +
9742     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9743     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9744     "&lt;/div&gt;&lt;hr /&gt;"
9745 );
9746
9747 var moreView = new Roo.JsonView({
9748     container :  "entry-list", 
9749     template : tpl,
9750     jsonRoot: "posts"
9751 });
9752 moreView.on("beforerender", this.sortEntries, this);
9753 moreView.load({
9754     url: "/blog/get-posts.php",
9755     params: "allposts=true",
9756     text: "Loading Blog Entries..."
9757 });
9758 </code></pre>
9759
9760 * Note: old code is supported with arguments : (container, template, config)
9761
9762
9763  * @constructor
9764  * Create a new JsonView
9765  * 
9766  * @param {Object} config The config object
9767  * 
9768  */
9769 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9770     
9771     
9772     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9773
9774     var um = this.el.getUpdateManager();
9775     um.setRenderer(this);
9776     um.on("update", this.onLoad, this);
9777     um.on("failure", this.onLoadException, this);
9778
9779     /**
9780      * @event beforerender
9781      * Fires before rendering of the downloaded JSON data.
9782      * @param {Roo.JsonView} this
9783      * @param {Object} data The JSON data loaded
9784      */
9785     /**
9786      * @event load
9787      * Fires when data is loaded.
9788      * @param {Roo.JsonView} this
9789      * @param {Object} data The JSON data loaded
9790      * @param {Object} response The raw Connect response object
9791      */
9792     /**
9793      * @event loadexception
9794      * Fires when loading fails.
9795      * @param {Roo.JsonView} this
9796      * @param {Object} response The raw Connect response object
9797      */
9798     this.addEvents({
9799         'beforerender' : true,
9800         'load' : true,
9801         'loadexception' : true
9802     });
9803 };
9804 Roo.extend(Roo.JsonView, Roo.View, {
9805     /**
9806      * @type {String} The root property in the loaded JSON object that contains the data
9807      */
9808     jsonRoot : "",
9809
9810     /**
9811      * Refreshes the view.
9812      */
9813     refresh : function(){
9814         this.clearSelections();
9815         this.el.update("");
9816         var html = [];
9817         var o = this.jsonData;
9818         if(o && o.length > 0){
9819             for(var i = 0, len = o.length; i < len; i++){
9820                 var data = this.prepareData(o[i], i, o);
9821                 html[html.length] = this.tpl.apply(data);
9822             }
9823         }else{
9824             html.push(this.emptyText);
9825         }
9826         this.el.update(html.join(""));
9827         this.nodes = this.el.dom.childNodes;
9828         this.updateIndexes(0);
9829     },
9830
9831     /**
9832      * Performs an async HTTP request, and loads the JSON from the response. If <i>params</i> are specified it uses POST, otherwise it uses GET.
9833      * @param {Object/String/Function} url The URL for this request, or a function to call to get the URL, or a config object containing any of the following options:
9834      <pre><code>
9835      view.load({
9836          url: "your-url.php",
9837          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9838          callback: yourFunction,
9839          scope: yourObject, //(optional scope)
9840          discardUrl: false,
9841          nocache: false,
9842          text: "Loading...",
9843          timeout: 30,
9844          scripts: false
9845      });
9846      </code></pre>
9847      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9848      * are respectively shorthand for <i>disableCaching</i>, <i>indicatorText</i>, and <i>loadScripts</i> and are used to set their associated property on this UpdateManager instance.
9849      * @param {String/Object} params (optional) The parameters to pass, as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
9850      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9851      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
9852      */
9853     load : function(){
9854         var um = this.el.getUpdateManager();
9855         um.update.apply(um, arguments);
9856     },
9857
9858     render : function(el, response){
9859         this.clearSelections();
9860         this.el.update("");
9861         var o;
9862         try{
9863             o = Roo.util.JSON.decode(response.responseText);
9864             if(this.jsonRoot){
9865                 
9866                 o = o[this.jsonRoot];
9867             }
9868         } catch(e){
9869         }
9870         /**
9871          * The current JSON data or null
9872          */
9873         this.jsonData = o;
9874         this.beforeRender();
9875         this.refresh();
9876     },
9877
9878 /**
9879  * Get the number of records in the current JSON dataset
9880  * @return {Number}
9881  */
9882     getCount : function(){
9883         return this.jsonData ? this.jsonData.length : 0;
9884     },
9885
9886 /**
9887  * Returns the JSON object for the specified node(s)
9888  * @param {HTMLElement/Array} node The node or an array of nodes
9889  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9890  * you get the JSON object for the node
9891  */
9892     getNodeData : function(node){
9893         if(node instanceof Array){
9894             var data = [];
9895             for(var i = 0, len = node.length; i < len; i++){
9896                 data.push(this.getNodeData(node[i]));
9897             }
9898             return data;
9899         }
9900         return this.jsonData[this.indexOf(node)] || null;
9901     },
9902
9903     beforeRender : function(){
9904         this.snapshot = this.jsonData;
9905         if(this.sortInfo){
9906             this.sort.apply(this, this.sortInfo);
9907         }
9908         this.fireEvent("beforerender", this, this.jsonData);
9909     },
9910
9911     onLoad : function(el, o){
9912         this.fireEvent("load", this, this.jsonData, o);
9913     },
9914
9915     onLoadException : function(el, o){
9916         this.fireEvent("loadexception", this, o);
9917     },
9918
9919 /**
9920  * Filter the data by a specific property.
9921  * @param {String} property A property on your JSON objects
9922  * @param {String/RegExp} value Either string that the property values
9923  * should start with, or a RegExp to test against the property
9924  */
9925     filter : function(property, value){
9926         if(this.jsonData){
9927             var data = [];
9928             var ss = this.snapshot;
9929             if(typeof value == "string"){
9930                 var vlen = value.length;
9931                 if(vlen == 0){
9932                     this.clearFilter();
9933                     return;
9934                 }
9935                 value = value.toLowerCase();
9936                 for(var i = 0, len = ss.length; i < len; i++){
9937                     var o = ss[i];
9938                     if(o[property].substr(0, vlen).toLowerCase() == value){
9939                         data.push(o);
9940                     }
9941                 }
9942             } else if(value.exec){ // regex?
9943                 for(var i = 0, len = ss.length; i < len; i++){
9944                     var o = ss[i];
9945                     if(value.test(o[property])){
9946                         data.push(o);
9947                     }
9948                 }
9949             } else{
9950                 return;
9951             }
9952             this.jsonData = data;
9953             this.refresh();
9954         }
9955     },
9956
9957 /**
9958  * Filter by a function. The passed function will be called with each
9959  * object in the current dataset. If the function returns true the value is kept,
9960  * otherwise it is filtered.
9961  * @param {Function} fn
9962  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9963  */
9964     filterBy : function(fn, scope){
9965         if(this.jsonData){
9966             var data = [];
9967             var ss = this.snapshot;
9968             for(var i = 0, len = ss.length; i < len; i++){
9969                 var o = ss[i];
9970                 if(fn.call(scope || this, o)){
9971                     data.push(o);
9972                 }
9973             }
9974             this.jsonData = data;
9975             this.refresh();
9976         }
9977     },
9978
9979 /**
9980  * Clears the current filter.
9981  */
9982     clearFilter : function(){
9983         if(this.snapshot && this.jsonData != this.snapshot){
9984             this.jsonData = this.snapshot;
9985             this.refresh();
9986         }
9987     },
9988
9989
9990 /**
9991  * Sorts the data for this view and refreshes it.
9992  * @param {String} property A property on your JSON objects to sort on
9993  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9994  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9995  */
9996     sort : function(property, dir, sortType){
9997         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9998         if(this.jsonData){
9999             var p = property;
10000             var dsc = dir && dir.toLowerCase() == "desc";
10001             var f = function(o1, o2){
10002                 var v1 = sortType ? sortType(o1[p]) : o1[p];
10003                 var v2 = sortType ? sortType(o2[p]) : o2[p];
10004                 ;
10005                 if(v1 < v2){
10006                     return dsc ? +1 : -1;
10007                 } else if(v1 > v2){
10008                     return dsc ? -1 : +1;
10009                 } else{
10010                     return 0;
10011                 }
10012             };
10013             this.jsonData.sort(f);
10014             this.refresh();
10015             if(this.jsonData != this.snapshot){
10016                 this.snapshot.sort(f);
10017             }
10018         }
10019     }
10020 });/*
10021  * Based on:
10022  * Ext JS Library 1.1.1
10023  * Copyright(c) 2006-2007, Ext JS, LLC.
10024  *
10025  * Originally Released Under LGPL - original licence link has changed is not relivant.
10026  *
10027  * Fork - LGPL
10028  * <script type="text/javascript">
10029  */
10030  
10031
10032 /**
10033  * @class Roo.ColorPalette
10034  * @extends Roo.Component
10035  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
10036  * Here's an example of typical usage:
10037  * <pre><code>
10038 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
10039 cp.render('my-div');
10040
10041 cp.on('select', function(palette, selColor){
10042     // do something with selColor
10043 });
10044 </code></pre>
10045  * @constructor
10046  * Create a new ColorPalette
10047  * @param {Object} config The config object
10048  */
10049 Roo.ColorPalette = function(config){
10050     Roo.ColorPalette.superclass.constructor.call(this, config);
10051     this.addEvents({
10052         /**
10053              * @event select
10054              * Fires when a color is selected
10055              * @param {ColorPalette} this
10056              * @param {String} color The 6-digit color hex code (without the # symbol)
10057              */
10058         select: true
10059     });
10060
10061     if(this.handler){
10062         this.on("select", this.handler, this.scope, true);
10063     }
10064 };
10065 Roo.extend(Roo.ColorPalette, Roo.Component, {
10066     /**
10067      * @cfg {String} itemCls
10068      * The CSS class to apply to the containing element (defaults to "x-color-palette")
10069      */
10070     itemCls : "x-color-palette",
10071     /**
10072      * @cfg {String} value
10073      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
10074      * the hex codes are case-sensitive.
10075      */
10076     value : null,
10077     clickEvent:'click',
10078     // private
10079     ctype: "Roo.ColorPalette",
10080
10081     /**
10082      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
10083      */
10084     allowReselect : false,
10085
10086     /**
10087      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
10088      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
10089      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
10090      * of colors with the width setting until the box is symmetrical.</p>
10091      * <p>You can override individual colors if needed:</p>
10092      * <pre><code>
10093 var cp = new Roo.ColorPalette();
10094 cp.colors[0] = "FF0000";  // change the first box to red
10095 </code></pre>
10096
10097 Or you can provide a custom array of your own for complete control:
10098 <pre><code>
10099 var cp = new Roo.ColorPalette();
10100 cp.colors = ["000000", "993300", "333300"];
10101 </code></pre>
10102      * @type Array
10103      */
10104     colors : [
10105         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
10106         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
10107         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
10108         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
10109         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
10110     ],
10111
10112     // private
10113     onRender : function(container, position){
10114         var t = new Roo.MasterTemplate(
10115             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
10116         );
10117         var c = this.colors;
10118         for(var i = 0, len = c.length; i < len; i++){
10119             t.add([c[i]]);
10120         }
10121         var el = document.createElement("div");
10122         el.className = this.itemCls;
10123         t.overwrite(el);
10124         container.dom.insertBefore(el, position);
10125         this.el = Roo.get(el);
10126         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
10127         if(this.clickEvent != 'click'){
10128             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
10129         }
10130     },
10131
10132     // private
10133     afterRender : function(){
10134         Roo.ColorPalette.superclass.afterRender.call(this);
10135         if(this.value){
10136             var s = this.value;
10137             this.value = null;
10138             this.select(s);
10139         }
10140     },
10141
10142     // private
10143     handleClick : function(e, t){
10144         e.preventDefault();
10145         if(!this.disabled){
10146             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
10147             this.select(c.toUpperCase());
10148         }
10149     },
10150
10151     /**
10152      * Selects the specified color in the palette (fires the select event)
10153      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
10154      */
10155     select : function(color){
10156         color = color.replace("#", "");
10157         if(color != this.value || this.allowReselect){
10158             var el = this.el;
10159             if(this.value){
10160                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
10161             }
10162             el.child("a.color-"+color).addClass("x-color-palette-sel");
10163             this.value = color;
10164             this.fireEvent("select", this, color);
10165         }
10166     }
10167 });/*
10168  * Based on:
10169  * Ext JS Library 1.1.1
10170  * Copyright(c) 2006-2007, Ext JS, LLC.
10171  *
10172  * Originally Released Under LGPL - original licence link has changed is not relivant.
10173  *
10174  * Fork - LGPL
10175  * <script type="text/javascript">
10176  */
10177  
10178 /**
10179  * @class Roo.DatePicker
10180  * @extends Roo.Component
10181  * Simple date picker class.
10182  * @constructor
10183  * Create a new DatePicker
10184  * @param {Object} config The config object
10185  */
10186 Roo.DatePicker = function(config){
10187     Roo.DatePicker.superclass.constructor.call(this, config);
10188
10189     this.value = config && config.value ?
10190                  config.value.clearTime() : new Date().clearTime();
10191
10192     this.addEvents({
10193         /**
10194              * @event select
10195              * Fires when a date is selected
10196              * @param {DatePicker} this
10197              * @param {Date} date The selected date
10198              */
10199         'select': true,
10200         /**
10201              * @event monthchange
10202              * Fires when the displayed month changes 
10203              * @param {DatePicker} this
10204              * @param {Date} date The selected month
10205              */
10206         'monthchange': true
10207     });
10208
10209     if(this.handler){
10210         this.on("select", this.handler,  this.scope || this);
10211     }
10212     // build the disabledDatesRE
10213     if(!this.disabledDatesRE && this.disabledDates){
10214         var dd = this.disabledDates;
10215         var re = "(?:";
10216         for(var i = 0; i < dd.length; i++){
10217             re += dd[i];
10218             if(i != dd.length-1) re += "|";
10219         }
10220         this.disabledDatesRE = new RegExp(re + ")");
10221     }
10222 };
10223
10224 Roo.extend(Roo.DatePicker, Roo.Component, {
10225     /**
10226      * @cfg {String} todayText
10227      * The text to display on the button that selects the current date (defaults to "Today")
10228      */
10229     todayText : "Today",
10230     /**
10231      * @cfg {String} okText
10232      * The text to display on the ok button
10233      */
10234     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10235     /**
10236      * @cfg {String} cancelText
10237      * The text to display on the cancel button
10238      */
10239     cancelText : "Cancel",
10240     /**
10241      * @cfg {String} todayTip
10242      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10243      */
10244     todayTip : "{0} (Spacebar)",
10245     /**
10246      * @cfg {Date} minDate
10247      * Minimum allowable date (JavaScript date object, defaults to null)
10248      */
10249     minDate : null,
10250     /**
10251      * @cfg {Date} maxDate
10252      * Maximum allowable date (JavaScript date object, defaults to null)
10253      */
10254     maxDate : null,
10255     /**
10256      * @cfg {String} minText
10257      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10258      */
10259     minText : "This date is before the minimum date",
10260     /**
10261      * @cfg {String} maxText
10262      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10263      */
10264     maxText : "This date is after the maximum date",
10265     /**
10266      * @cfg {String} format
10267      * The default date format string which can be overriden for localization support.  The format must be
10268      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10269      */
10270     format : "m/d/y",
10271     /**
10272      * @cfg {Array} disabledDays
10273      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10274      */
10275     disabledDays : null,
10276     /**
10277      * @cfg {String} disabledDaysText
10278      * The tooltip to display when the date falls on a disabled day (defaults to "")
10279      */
10280     disabledDaysText : "",
10281     /**
10282      * @cfg {RegExp} disabledDatesRE
10283      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10284      */
10285     disabledDatesRE : null,
10286     /**
10287      * @cfg {String} disabledDatesText
10288      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10289      */
10290     disabledDatesText : "",
10291     /**
10292      * @cfg {Boolean} constrainToViewport
10293      * True to constrain the date picker to the viewport (defaults to true)
10294      */
10295     constrainToViewport : true,
10296     /**
10297      * @cfg {Array} monthNames
10298      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10299      */
10300     monthNames : Date.monthNames,
10301     /**
10302      * @cfg {Array} dayNames
10303      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10304      */
10305     dayNames : Date.dayNames,
10306     /**
10307      * @cfg {String} nextText
10308      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10309      */
10310     nextText: 'Next Month (Control+Right)',
10311     /**
10312      * @cfg {String} prevText
10313      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10314      */
10315     prevText: 'Previous Month (Control+Left)',
10316     /**
10317      * @cfg {String} monthYearText
10318      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10319      */
10320     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10321     /**
10322      * @cfg {Number} startDay
10323      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10324      */
10325     startDay : 0,
10326     /**
10327      * @cfg {Bool} showClear
10328      * Show a clear button (usefull for date form elements that can be blank.)
10329      */
10330     
10331     showClear: false,
10332     
10333     /**
10334      * Sets the value of the date field
10335      * @param {Date} value The date to set
10336      */
10337     setValue : function(value){
10338         var old = this.value;
10339         if (typeof(value) == 'string') {
10340             value = Date.parseDate(value, this.format);
10341         }
10342         
10343         this.value = value.clearTime(true);
10344         if(this.el){
10345             this.update(this.value);
10346         }
10347     },
10348
10349     /**
10350      * Gets the current selected value of the date field
10351      * @return {Date} The selected date
10352      */
10353     getValue : function(){
10354         return this.value;
10355     },
10356
10357     // private
10358     focus : function(){
10359         if(this.el){
10360             this.update(this.activeDate);
10361         }
10362     },
10363
10364     // private
10365     onRender : function(container, position){
10366         
10367         var m = [
10368              '<table cellspacing="0">',
10369                 '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'">&#160;</a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'">&#160;</a></td></tr>',
10370                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10371         var dn = this.dayNames;
10372         for(var i = 0; i < 7; i++){
10373             var d = this.startDay+i;
10374             if(d > 6){
10375                 d = d-7;
10376             }
10377             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10378         }
10379         m[m.length] = "</tr></thead><tbody><tr>";
10380         for(var i = 0; i < 42; i++) {
10381             if(i % 7 == 0 && i != 0){
10382                 m[m.length] = "</tr><tr>";
10383             }
10384             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10385         }
10386         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10387             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10388
10389         var el = document.createElement("div");
10390         el.className = "x-date-picker";
10391         el.innerHTML = m.join("");
10392
10393         container.dom.insertBefore(el, position);
10394
10395         this.el = Roo.get(el);
10396         this.eventEl = Roo.get(el.firstChild);
10397
10398         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10399             handler: this.showPrevMonth,
10400             scope: this,
10401             preventDefault:true,
10402             stopDefault:true
10403         });
10404
10405         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10406             handler: this.showNextMonth,
10407             scope: this,
10408             preventDefault:true,
10409             stopDefault:true
10410         });
10411
10412         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10413
10414         this.monthPicker = this.el.down('div.x-date-mp');
10415         this.monthPicker.enableDisplayMode('block');
10416         
10417         var kn = new Roo.KeyNav(this.eventEl, {
10418             "left" : function(e){
10419                 e.ctrlKey ?
10420                     this.showPrevMonth() :
10421                     this.update(this.activeDate.add("d", -1));
10422             },
10423
10424             "right" : function(e){
10425                 e.ctrlKey ?
10426                     this.showNextMonth() :
10427                     this.update(this.activeDate.add("d", 1));
10428             },
10429
10430             "up" : function(e){
10431                 e.ctrlKey ?
10432                     this.showNextYear() :
10433                     this.update(this.activeDate.add("d", -7));
10434             },
10435
10436             "down" : function(e){
10437                 e.ctrlKey ?
10438                     this.showPrevYear() :
10439                     this.update(this.activeDate.add("d", 7));
10440             },
10441
10442             "pageUp" : function(e){
10443                 this.showNextMonth();
10444             },
10445
10446             "pageDown" : function(e){
10447                 this.showPrevMonth();
10448             },
10449
10450             "enter" : function(e){
10451                 e.stopPropagation();
10452                 return true;
10453             },
10454
10455             scope : this
10456         });
10457
10458         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10459
10460         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10461
10462         this.el.unselectable();
10463         
10464         this.cells = this.el.select("table.x-date-inner tbody td");
10465         this.textNodes = this.el.query("table.x-date-inner tbody span");
10466
10467         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10468             text: "&#160;",
10469             tooltip: this.monthYearText
10470         });
10471
10472         this.mbtn.on('click', this.showMonthPicker, this);
10473         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10474
10475
10476         var today = (new Date()).dateFormat(this.format);
10477         
10478         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10479         if (this.showClear) {
10480             baseTb.add( new Roo.Toolbar.Fill());
10481         }
10482         baseTb.add({
10483             text: String.format(this.todayText, today),
10484             tooltip: String.format(this.todayTip, today),
10485             handler: this.selectToday,
10486             scope: this
10487         });
10488         
10489         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10490             
10491         //});
10492         if (this.showClear) {
10493             
10494             baseTb.add( new Roo.Toolbar.Fill());
10495             baseTb.add({
10496                 text: '&#160;',
10497                 cls: 'x-btn-icon x-btn-clear',
10498                 handler: function() {
10499                     //this.value = '';
10500                     this.fireEvent("select", this, '');
10501                 },
10502                 scope: this
10503             });
10504         }
10505         
10506         
10507         if(Roo.isIE){
10508             this.el.repaint();
10509         }
10510         this.update(this.value);
10511     },
10512
10513     createMonthPicker : function(){
10514         if(!this.monthPicker.dom.firstChild){
10515             var buf = ['<table border="0" cellspacing="0">'];
10516             for(var i = 0; i < 6; i++){
10517                 buf.push(
10518                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10519                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10520                     i == 0 ?
10521                     '<td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-prev"></a></td><td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-next"></a></td></tr>' :
10522                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10523                 );
10524             }
10525             buf.push(
10526                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10527                     this.okText,
10528                     '</button><button type="button" class="x-date-mp-cancel">',
10529                     this.cancelText,
10530                     '</button></td></tr>',
10531                 '</table>'
10532             );
10533             this.monthPicker.update(buf.join(''));
10534             this.monthPicker.on('click', this.onMonthClick, this);
10535             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10536
10537             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10538             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10539
10540             this.mpMonths.each(function(m, a, i){
10541                 i += 1;
10542                 if((i%2) == 0){
10543                     m.dom.xmonth = 5 + Math.round(i * .5);
10544                 }else{
10545                     m.dom.xmonth = Math.round((i-1) * .5);
10546                 }
10547             });
10548         }
10549     },
10550
10551     showMonthPicker : function(){
10552         this.createMonthPicker();
10553         var size = this.el.getSize();
10554         this.monthPicker.setSize(size);
10555         this.monthPicker.child('table').setSize(size);
10556
10557         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10558         this.updateMPMonth(this.mpSelMonth);
10559         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10560         this.updateMPYear(this.mpSelYear);
10561
10562         this.monthPicker.slideIn('t', {duration:.2});
10563     },
10564
10565     updateMPYear : function(y){
10566         this.mpyear = y;
10567         var ys = this.mpYears.elements;
10568         for(var i = 1; i <= 10; i++){
10569             var td = ys[i-1], y2;
10570             if((i%2) == 0){
10571                 y2 = y + Math.round(i * .5);
10572                 td.firstChild.innerHTML = y2;
10573                 td.xyear = y2;
10574             }else{
10575                 y2 = y - (5-Math.round(i * .5));
10576                 td.firstChild.innerHTML = y2;
10577                 td.xyear = y2;
10578             }
10579             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10580         }
10581     },
10582
10583     updateMPMonth : function(sm){
10584         this.mpMonths.each(function(m, a, i){
10585             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10586         });
10587     },
10588
10589     selectMPMonth: function(m){
10590         
10591     },
10592
10593     onMonthClick : function(e, t){
10594         e.stopEvent();
10595         var el = new Roo.Element(t), pn;
10596         if(el.is('button.x-date-mp-cancel')){
10597             this.hideMonthPicker();
10598         }
10599         else if(el.is('button.x-date-mp-ok')){
10600             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10601             this.hideMonthPicker();
10602         }
10603         else if(pn = el.up('td.x-date-mp-month', 2)){
10604             this.mpMonths.removeClass('x-date-mp-sel');
10605             pn.addClass('x-date-mp-sel');
10606             this.mpSelMonth = pn.dom.xmonth;
10607         }
10608         else if(pn = el.up('td.x-date-mp-year', 2)){
10609             this.mpYears.removeClass('x-date-mp-sel');
10610             pn.addClass('x-date-mp-sel');
10611             this.mpSelYear = pn.dom.xyear;
10612         }
10613         else if(el.is('a.x-date-mp-prev')){
10614             this.updateMPYear(this.mpyear-10);
10615         }
10616         else if(el.is('a.x-date-mp-next')){
10617             this.updateMPYear(this.mpyear+10);
10618         }
10619     },
10620
10621     onMonthDblClick : function(e, t){
10622         e.stopEvent();
10623         var el = new Roo.Element(t), pn;
10624         if(pn = el.up('td.x-date-mp-month', 2)){
10625             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10626             this.hideMonthPicker();
10627         }
10628         else if(pn = el.up('td.x-date-mp-year', 2)){
10629             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10630             this.hideMonthPicker();
10631         }
10632     },
10633
10634     hideMonthPicker : function(disableAnim){
10635         if(this.monthPicker){
10636             if(disableAnim === true){
10637                 this.monthPicker.hide();
10638             }else{
10639                 this.monthPicker.slideOut('t', {duration:.2});
10640             }
10641         }
10642     },
10643
10644     // private
10645     showPrevMonth : function(e){
10646         this.update(this.activeDate.add("mo", -1));
10647     },
10648
10649     // private
10650     showNextMonth : function(e){
10651         this.update(this.activeDate.add("mo", 1));
10652     },
10653
10654     // private
10655     showPrevYear : function(){
10656         this.update(this.activeDate.add("y", -1));
10657     },
10658
10659     // private
10660     showNextYear : function(){
10661         this.update(this.activeDate.add("y", 1));
10662     },
10663
10664     // private
10665     handleMouseWheel : function(e){
10666         var delta = e.getWheelDelta();
10667         if(delta > 0){
10668             this.showPrevMonth();
10669             e.stopEvent();
10670         } else if(delta < 0){
10671             this.showNextMonth();
10672             e.stopEvent();
10673         }
10674     },
10675
10676     // private
10677     handleDateClick : function(e, t){
10678         e.stopEvent();
10679         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10680             this.setValue(new Date(t.dateValue));
10681             this.fireEvent("select", this, this.value);
10682         }
10683     },
10684
10685     // private
10686     selectToday : function(){
10687         this.setValue(new Date().clearTime());
10688         this.fireEvent("select", this, this.value);
10689     },
10690
10691     // private
10692     update : function(date)
10693     {
10694         var vd = this.activeDate;
10695         this.activeDate = date;
10696         if(vd && this.el){
10697             var t = date.getTime();
10698             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10699                 this.cells.removeClass("x-date-selected");
10700                 this.cells.each(function(c){
10701                    if(c.dom.firstChild.dateValue == t){
10702                        c.addClass("x-date-selected");
10703                        setTimeout(function(){
10704                             try{c.dom.firstChild.focus();}catch(e){}
10705                        }, 50);
10706                        return false;
10707                    }
10708                 });
10709                 return;
10710             }
10711         }
10712         
10713         var days = date.getDaysInMonth();
10714         var firstOfMonth = date.getFirstDateOfMonth();
10715         var startingPos = firstOfMonth.getDay()-this.startDay;
10716
10717         if(startingPos <= this.startDay){
10718             startingPos += 7;
10719         }
10720
10721         var pm = date.add("mo", -1);
10722         var prevStart = pm.getDaysInMonth()-startingPos;
10723
10724         var cells = this.cells.elements;
10725         var textEls = this.textNodes;
10726         days += startingPos;
10727
10728         // convert everything to numbers so it's fast
10729         var day = 86400000;
10730         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10731         var today = new Date().clearTime().getTime();
10732         var sel = date.clearTime().getTime();
10733         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10734         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10735         var ddMatch = this.disabledDatesRE;
10736         var ddText = this.disabledDatesText;
10737         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10738         var ddaysText = this.disabledDaysText;
10739         var format = this.format;
10740
10741         var setCellClass = function(cal, cell){
10742             cell.title = "";
10743             var t = d.getTime();
10744             cell.firstChild.dateValue = t;
10745             if(t == today){
10746                 cell.className += " x-date-today";
10747                 cell.title = cal.todayText;
10748             }
10749             if(t == sel){
10750                 cell.className += " x-date-selected";
10751                 setTimeout(function(){
10752                     try{cell.firstChild.focus();}catch(e){}
10753                 }, 50);
10754             }
10755             // disabling
10756             if(t < min) {
10757                 cell.className = " x-date-disabled";
10758                 cell.title = cal.minText;
10759                 return;
10760             }
10761             if(t > max) {
10762                 cell.className = " x-date-disabled";
10763                 cell.title = cal.maxText;
10764                 return;
10765             }
10766             if(ddays){
10767                 if(ddays.indexOf(d.getDay()) != -1){
10768                     cell.title = ddaysText;
10769                     cell.className = " x-date-disabled";
10770                 }
10771             }
10772             if(ddMatch && format){
10773                 var fvalue = d.dateFormat(format);
10774                 if(ddMatch.test(fvalue)){
10775                     cell.title = ddText.replace("%0", fvalue);
10776                     cell.className = " x-date-disabled";
10777                 }
10778             }
10779         };
10780
10781         var i = 0;
10782         for(; i < startingPos; i++) {
10783             textEls[i].innerHTML = (++prevStart);
10784             d.setDate(d.getDate()+1);
10785             cells[i].className = "x-date-prevday";
10786             setCellClass(this, cells[i]);
10787         }
10788         for(; i < days; i++){
10789             intDay = i - startingPos + 1;
10790             textEls[i].innerHTML = (intDay);
10791             d.setDate(d.getDate()+1);
10792             cells[i].className = "x-date-active";
10793             setCellClass(this, cells[i]);
10794         }
10795         var extraDays = 0;
10796         for(; i < 42; i++) {
10797              textEls[i].innerHTML = (++extraDays);
10798              d.setDate(d.getDate()+1);
10799              cells[i].className = "x-date-nextday";
10800              setCellClass(this, cells[i]);
10801         }
10802
10803         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10804         this.fireEvent('monthchange', this, date);
10805         
10806         if(!this.internalRender){
10807             var main = this.el.dom.firstChild;
10808             var w = main.offsetWidth;
10809             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10810             Roo.fly(main).setWidth(w);
10811             this.internalRender = true;
10812             // opera does not respect the auto grow header center column
10813             // then, after it gets a width opera refuses to recalculate
10814             // without a second pass
10815             if(Roo.isOpera && !this.secondPass){
10816                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10817                 this.secondPass = true;
10818                 this.update.defer(10, this, [date]);
10819             }
10820         }
10821         
10822         
10823     }
10824 });        /*
10825  * Based on:
10826  * Ext JS Library 1.1.1
10827  * Copyright(c) 2006-2007, Ext JS, LLC.
10828  *
10829  * Originally Released Under LGPL - original licence link has changed is not relivant.
10830  *
10831  * Fork - LGPL
10832  * <script type="text/javascript">
10833  */
10834 /**
10835  * @class Roo.TabPanel
10836  * @extends Roo.util.Observable
10837  * A lightweight tab container.
10838  * <br><br>
10839  * Usage:
10840  * <pre><code>
10841 // basic tabs 1, built from existing content
10842 var tabs = new Roo.TabPanel("tabs1");
10843 tabs.addTab("script", "View Script");
10844 tabs.addTab("markup", "View Markup");
10845 tabs.activate("script");
10846
10847 // more advanced tabs, built from javascript
10848 var jtabs = new Roo.TabPanel("jtabs");
10849 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10850
10851 // set up the UpdateManager
10852 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10853 var updater = tab2.getUpdateManager();
10854 updater.setDefaultUrl("ajax1.htm");
10855 tab2.on('activate', updater.refresh, updater, true);
10856
10857 // Use setUrl for Ajax loading
10858 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10859 tab3.setUrl("ajax2.htm", null, true);
10860
10861 // Disabled tab
10862 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10863 tab4.disable();
10864
10865 jtabs.activate("jtabs-1");
10866  * </code></pre>
10867  * @constructor
10868  * Create a new TabPanel.
10869  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10870  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10871  */
10872 Roo.TabPanel = function(container, config){
10873     /**
10874     * The container element for this TabPanel.
10875     * @type Roo.Element
10876     */
10877     this.el = Roo.get(container, true);
10878     if(config){
10879         if(typeof config == "boolean"){
10880             this.tabPosition = config ? "bottom" : "top";
10881         }else{
10882             Roo.apply(this, config);
10883         }
10884     }
10885     if(this.tabPosition == "bottom"){
10886         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10887         this.el.addClass("x-tabs-bottom");
10888     }
10889     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10890     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10891     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10892     if(Roo.isIE){
10893         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10894     }
10895     if(this.tabPosition != "bottom"){
10896         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
10897          * @type Roo.Element
10898          */
10899         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10900         this.el.addClass("x-tabs-top");
10901     }
10902     this.items = [];
10903
10904     this.bodyEl.setStyle("position", "relative");
10905
10906     this.active = null;
10907     this.activateDelegate = this.activate.createDelegate(this);
10908
10909     this.addEvents({
10910         /**
10911          * @event tabchange
10912          * Fires when the active tab changes
10913          * @param {Roo.TabPanel} this
10914          * @param {Roo.TabPanelItem} activePanel The new active tab
10915          */
10916         "tabchange": true,
10917         /**
10918          * @event beforetabchange
10919          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10920          * @param {Roo.TabPanel} this
10921          * @param {Object} e Set cancel to true on this object to cancel the tab change
10922          * @param {Roo.TabPanelItem} tab The tab being changed to
10923          */
10924         "beforetabchange" : true
10925     });
10926
10927     Roo.EventManager.onWindowResize(this.onResize, this);
10928     this.cpad = this.el.getPadding("lr");
10929     this.hiddenCount = 0;
10930
10931
10932     // toolbar on the tabbar support...
10933     if (this.toolbar) {
10934         var tcfg = this.toolbar;
10935         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
10936         this.toolbar = new Roo.Toolbar(tcfg);
10937         if (Roo.isSafari) {
10938             var tbl = tcfg.container.child('table', true);
10939             tbl.setAttribute('width', '100%');
10940         }
10941         
10942     }
10943    
10944
10945
10946     Roo.TabPanel.superclass.constructor.call(this);
10947 };
10948
10949 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10950     /*
10951      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10952      */
10953     tabPosition : "top",
10954     /*
10955      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10956      */
10957     currentTabWidth : 0,
10958     /*
10959      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10960      */
10961     minTabWidth : 40,
10962     /*
10963      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10964      */
10965     maxTabWidth : 250,
10966     /*
10967      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10968      */
10969     preferredTabWidth : 175,
10970     /*
10971      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10972      */
10973     resizeTabs : false,
10974     /*
10975      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10976      */
10977     monitorResize : true,
10978     /*
10979      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
10980      */
10981     toolbar : false,
10982
10983     /**
10984      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10985      * @param {String} id The id of the div to use <b>or create</b>
10986      * @param {String} text The text for the tab
10987      * @param {String} content (optional) Content to put in the TabPanelItem body
10988      * @param {Boolean} closable (optional) True to create a close icon on the tab
10989      * @return {Roo.TabPanelItem} The created TabPanelItem
10990      */
10991     addTab : function(id, text, content, closable){
10992         var item = new Roo.TabPanelItem(this, id, text, closable);
10993         this.addTabItem(item);
10994         if(content){
10995             item.setContent(content);
10996         }
10997         return item;
10998     },
10999
11000     /**
11001      * Returns the {@link Roo.TabPanelItem} with the specified id/index
11002      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
11003      * @return {Roo.TabPanelItem}
11004      */
11005     getTab : function(id){
11006         return this.items[id];
11007     },
11008
11009     /**
11010      * Hides the {@link Roo.TabPanelItem} with the specified id/index
11011      * @param {String/Number} id The id or index of the TabPanelItem to hide.
11012      */
11013     hideTab : function(id){
11014         var t = this.items[id];
11015         if(!t.isHidden()){
11016            t.setHidden(true);
11017            this.hiddenCount++;
11018            this.autoSizeTabs();
11019         }
11020     },
11021
11022     /**
11023      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
11024      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
11025      */
11026     unhideTab : function(id){
11027         var t = this.items[id];
11028         if(t.isHidden()){
11029            t.setHidden(false);
11030            this.hiddenCount--;
11031            this.autoSizeTabs();
11032         }
11033     },
11034
11035     /**
11036      * Adds an existing {@link Roo.TabPanelItem}.
11037      * @param {Roo.TabPanelItem} item The TabPanelItem to add
11038      */
11039     addTabItem : function(item){
11040         this.items[item.id] = item;
11041         this.items.push(item);
11042         if(this.resizeTabs){
11043            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
11044            this.autoSizeTabs();
11045         }else{
11046             item.autoSize();
11047         }
11048     },
11049
11050     /**
11051      * Removes a {@link Roo.TabPanelItem}.
11052      * @param {String/Number} id The id or index of the TabPanelItem to remove.
11053      */
11054     removeTab : function(id){
11055         var items = this.items;
11056         var tab = items[id];
11057         if(!tab) { return; }
11058         var index = items.indexOf(tab);
11059         if(this.active == tab && items.length > 1){
11060             var newTab = this.getNextAvailable(index);
11061             if(newTab) {
11062                 newTab.activate();
11063             }
11064         }
11065         this.stripEl.dom.removeChild(tab.pnode.dom);
11066         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
11067             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
11068         }
11069         items.splice(index, 1);
11070         delete this.items[tab.id];
11071         tab.fireEvent("close", tab);
11072         tab.purgeListeners();
11073         this.autoSizeTabs();
11074     },
11075
11076     getNextAvailable : function(start){
11077         var items = this.items;
11078         var index = start;
11079         // look for a next tab that will slide over to
11080         // replace the one being removed
11081         while(index < items.length){
11082             var item = items[++index];
11083             if(item && !item.isHidden()){
11084                 return item;
11085             }
11086         }
11087         // if one isn't found select the previous tab (on the left)
11088         index = start;
11089         while(index >= 0){
11090             var item = items[--index];
11091             if(item && !item.isHidden()){
11092                 return item;
11093             }
11094         }
11095         return null;
11096     },
11097
11098     /**
11099      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
11100      * @param {String/Number} id The id or index of the TabPanelItem to disable.
11101      */
11102     disableTab : function(id){
11103         var tab = this.items[id];
11104         if(tab && this.active != tab){
11105             tab.disable();
11106         }
11107     },
11108
11109     /**
11110      * Enables a {@link Roo.TabPanelItem} that is disabled.
11111      * @param {String/Number} id The id or index of the TabPanelItem to enable.
11112      */
11113     enableTab : function(id){
11114         var tab = this.items[id];
11115         tab.enable();
11116     },
11117
11118     /**
11119      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
11120      * @param {String/Number} id The id or index of the TabPanelItem to activate.
11121      * @return {Roo.TabPanelItem} The TabPanelItem.
11122      */
11123     activate : function(id){
11124         var tab = this.items[id];
11125         if(!tab){
11126             return null;
11127         }
11128         if(tab == this.active || tab.disabled){
11129             return tab;
11130         }
11131         var e = {};
11132         this.fireEvent("beforetabchange", this, e, tab);
11133         if(e.cancel !== true && !tab.disabled){
11134             if(this.active){
11135                 this.active.hide();
11136             }
11137             this.active = this.items[id];
11138             this.active.show();
11139             this.fireEvent("tabchange", this, this.active);
11140         }
11141         return tab;
11142     },
11143
11144     /**
11145      * Gets the active {@link Roo.TabPanelItem}.
11146      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
11147      */
11148     getActiveTab : function(){
11149         return this.active;
11150     },
11151
11152     /**
11153      * Updates the tab body element to fit the height of the container element
11154      * for overflow scrolling
11155      * @param {Number} targetHeight (optional) Override the starting height from the elements height
11156      */
11157     syncHeight : function(targetHeight){
11158         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
11159         var bm = this.bodyEl.getMargins();
11160         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
11161         this.bodyEl.setHeight(newHeight);
11162         return newHeight;
11163     },
11164
11165     onResize : function(){
11166         if(this.monitorResize){
11167             this.autoSizeTabs();
11168         }
11169     },
11170
11171     /**
11172      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
11173      */
11174     beginUpdate : function(){
11175         this.updating = true;
11176     },
11177
11178     /**
11179      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
11180      */
11181     endUpdate : function(){
11182         this.updating = false;
11183         this.autoSizeTabs();
11184     },
11185
11186     /**
11187      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
11188      */
11189     autoSizeTabs : function(){
11190         var count = this.items.length;
11191         var vcount = count - this.hiddenCount;
11192         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
11193         var w = Math.max(this.el.getWidth() - this.cpad, 10);
11194         var availWidth = Math.floor(w / vcount);
11195         var b = this.stripBody;
11196         if(b.getWidth() > w){
11197             var tabs = this.items;
11198             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
11199             if(availWidth < this.minTabWidth){
11200                 /*if(!this.sleft){    // incomplete scrolling code
11201                     this.createScrollButtons();
11202                 }
11203                 this.showScroll();
11204                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
11205             }
11206         }else{
11207             if(this.currentTabWidth < this.preferredTabWidth){
11208                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
11209             }
11210         }
11211     },
11212
11213     /**
11214      * Returns the number of tabs in this TabPanel.
11215      * @return {Number}
11216      */
11217      getCount : function(){
11218          return this.items.length;
11219      },
11220
11221     /**
11222      * Resizes all the tabs to the passed width
11223      * @param {Number} The new width
11224      */
11225     setTabWidth : function(width){
11226         this.currentTabWidth = width;
11227         for(var i = 0, len = this.items.length; i < len; i++) {
11228                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
11229         }
11230     },
11231
11232     /**
11233      * Destroys this TabPanel
11234      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
11235      */
11236     destroy : function(removeEl){
11237         Roo.EventManager.removeResizeListener(this.onResize, this);
11238         for(var i = 0, len = this.items.length; i < len; i++){
11239             this.items[i].purgeListeners();
11240         }
11241         if(removeEl === true){
11242             this.el.update("");
11243             this.el.remove();
11244         }
11245     }
11246 });
11247
11248 /**
11249  * @class Roo.TabPanelItem
11250  * @extends Roo.util.Observable
11251  * Represents an individual item (tab plus body) in a TabPanel.
11252  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11253  * @param {String} id The id of this TabPanelItem
11254  * @param {String} text The text for the tab of this TabPanelItem
11255  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11256  */
11257 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11258     /**
11259      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11260      * @type Roo.TabPanel
11261      */
11262     this.tabPanel = tabPanel;
11263     /**
11264      * The id for this TabPanelItem
11265      * @type String
11266      */
11267     this.id = id;
11268     /** @private */
11269     this.disabled = false;
11270     /** @private */
11271     this.text = text;
11272     /** @private */
11273     this.loaded = false;
11274     this.closable = closable;
11275
11276     /**
11277      * The body element for this TabPanelItem.
11278      * @type Roo.Element
11279      */
11280     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11281     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11282     this.bodyEl.setStyle("display", "block");
11283     this.bodyEl.setStyle("zoom", "1");
11284     this.hideAction();
11285
11286     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11287     /** @private */
11288     this.el = Roo.get(els.el, true);
11289     this.inner = Roo.get(els.inner, true);
11290     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11291     this.pnode = Roo.get(els.el.parentNode, true);
11292     this.el.on("mousedown", this.onTabMouseDown, this);
11293     this.el.on("click", this.onTabClick, this);
11294     /** @private */
11295     if(closable){
11296         var c = Roo.get(els.close, true);
11297         c.dom.title = this.closeText;
11298         c.addClassOnOver("close-over");
11299         c.on("click", this.closeClick, this);
11300      }
11301
11302     this.addEvents({
11303          /**
11304          * @event activate
11305          * Fires when this tab becomes the active tab.
11306          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11307          * @param {Roo.TabPanelItem} this
11308          */
11309         "activate": true,
11310         /**
11311          * @event beforeclose
11312          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11313          * @param {Roo.TabPanelItem} this
11314          * @param {Object} e Set cancel to true on this object to cancel the close.
11315          */
11316         "beforeclose": true,
11317         /**
11318          * @event close
11319          * Fires when this tab is closed.
11320          * @param {Roo.TabPanelItem} this
11321          */
11322          "close": true,
11323         /**
11324          * @event deactivate
11325          * Fires when this tab is no longer the active tab.
11326          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11327          * @param {Roo.TabPanelItem} this
11328          */
11329          "deactivate" : true
11330     });
11331     this.hidden = false;
11332
11333     Roo.TabPanelItem.superclass.constructor.call(this);
11334 };
11335
11336 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11337     purgeListeners : function(){
11338        Roo.util.Observable.prototype.purgeListeners.call(this);
11339        this.el.removeAllListeners();
11340     },
11341     /**
11342      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11343      */
11344     show : function(){
11345         this.pnode.addClass("on");
11346         this.showAction();
11347         if(Roo.isOpera){
11348             this.tabPanel.stripWrap.repaint();
11349         }
11350         this.fireEvent("activate", this.tabPanel, this);
11351     },
11352
11353     /**
11354      * Returns true if this tab is the active tab.
11355      * @return {Boolean}
11356      */
11357     isActive : function(){
11358         return this.tabPanel.getActiveTab() == this;
11359     },
11360
11361     /**
11362      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11363      */
11364     hide : function(){
11365         this.pnode.removeClass("on");
11366         this.hideAction();
11367         this.fireEvent("deactivate", this.tabPanel, this);
11368     },
11369
11370     hideAction : function(){
11371         this.bodyEl.hide();
11372         this.bodyEl.setStyle("position", "absolute");
11373         this.bodyEl.setLeft("-20000px");
11374         this.bodyEl.setTop("-20000px");
11375     },
11376
11377     showAction : function(){
11378         this.bodyEl.setStyle("position", "relative");
11379         this.bodyEl.setTop("");
11380         this.bodyEl.setLeft("");
11381         this.bodyEl.show();
11382     },
11383
11384     /**
11385      * Set the tooltip for the tab.
11386      * @param {String} tooltip The tab's tooltip
11387      */
11388     setTooltip : function(text){
11389         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11390             this.textEl.dom.qtip = text;
11391             this.textEl.dom.removeAttribute('title');
11392         }else{
11393             this.textEl.dom.title = text;
11394         }
11395     },
11396
11397     onTabClick : function(e){
11398         e.preventDefault();
11399         this.tabPanel.activate(this.id);
11400     },
11401
11402     onTabMouseDown : function(e){
11403         e.preventDefault();
11404         this.tabPanel.activate(this.id);
11405     },
11406
11407     getWidth : function(){
11408         return this.inner.getWidth();
11409     },
11410
11411     setWidth : function(width){
11412         var iwidth = width - this.pnode.getPadding("lr");
11413         this.inner.setWidth(iwidth);
11414         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11415         this.pnode.setWidth(width);
11416     },
11417
11418     /**
11419      * Show or hide the tab
11420      * @param {Boolean} hidden True to hide or false to show.
11421      */
11422     setHidden : function(hidden){
11423         this.hidden = hidden;
11424         this.pnode.setStyle("display", hidden ? "none" : "");
11425     },
11426
11427     /**
11428      * Returns true if this tab is "hidden"
11429      * @return {Boolean}
11430      */
11431     isHidden : function(){
11432         return this.hidden;
11433     },
11434
11435     /**
11436      * Returns the text for this tab
11437      * @return {String}
11438      */
11439     getText : function(){
11440         return this.text;
11441     },
11442
11443     autoSize : function(){
11444         //this.el.beginMeasure();
11445         this.textEl.setWidth(1);
11446         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11447         //this.el.endMeasure();
11448     },
11449
11450     /**
11451      * Sets the text for the tab (Note: this also sets the tooltip text)
11452      * @param {String} text The tab's text and tooltip
11453      */
11454     setText : function(text){
11455         this.text = text;
11456         this.textEl.update(text);
11457         this.setTooltip(text);
11458         if(!this.tabPanel.resizeTabs){
11459             this.autoSize();
11460         }
11461     },
11462     /**
11463      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11464      */
11465     activate : function(){
11466         this.tabPanel.activate(this.id);
11467     },
11468
11469     /**
11470      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11471      */
11472     disable : function(){
11473         if(this.tabPanel.active != this){
11474             this.disabled = true;
11475             this.pnode.addClass("disabled");
11476         }
11477     },
11478
11479     /**
11480      * Enables this TabPanelItem if it was previously disabled.
11481      */
11482     enable : function(){
11483         this.disabled = false;
11484         this.pnode.removeClass("disabled");
11485     },
11486
11487     /**
11488      * Sets the content for this TabPanelItem.
11489      * @param {String} content The content
11490      * @param {Boolean} loadScripts true to look for and load scripts
11491      */
11492     setContent : function(content, loadScripts){
11493         this.bodyEl.update(content, loadScripts);
11494     },
11495
11496     /**
11497      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11498      * @return {Roo.UpdateManager} The UpdateManager
11499      */
11500     getUpdateManager : function(){
11501         return this.bodyEl.getUpdateManager();
11502     },
11503
11504     /**
11505      * Set a URL to be used to load the content for this TabPanelItem.
11506      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11507      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
11508      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
11509      * @return {Roo.UpdateManager} The UpdateManager
11510      */
11511     setUrl : function(url, params, loadOnce){
11512         if(this.refreshDelegate){
11513             this.un('activate', this.refreshDelegate);
11514         }
11515         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11516         this.on("activate", this.refreshDelegate);
11517         return this.bodyEl.getUpdateManager();
11518     },
11519
11520     /** @private */
11521     _handleRefresh : function(url, params, loadOnce){
11522         if(!loadOnce || !this.loaded){
11523             var updater = this.bodyEl.getUpdateManager();
11524             updater.update(url, params, this._setLoaded.createDelegate(this));
11525         }
11526     },
11527
11528     /**
11529      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11530      *   Will fail silently if the setUrl method has not been called.
11531      *   This does not activate the panel, just updates its content.
11532      */
11533     refresh : function(){
11534         if(this.refreshDelegate){
11535            this.loaded = false;
11536            this.refreshDelegate();
11537         }
11538     },
11539
11540     /** @private */
11541     _setLoaded : function(){
11542         this.loaded = true;
11543     },
11544
11545     /** @private */
11546     closeClick : function(e){
11547         var o = {};
11548         e.stopEvent();
11549         this.fireEvent("beforeclose", this, o);
11550         if(o.cancel !== true){
11551             this.tabPanel.removeTab(this.id);
11552         }
11553     },
11554     /**
11555      * The text displayed in the tooltip for the close icon.
11556      * @type String
11557      */
11558     closeText : "Close this tab"
11559 });
11560
11561 /** @private */
11562 Roo.TabPanel.prototype.createStrip = function(container){
11563     var strip = document.createElement("div");
11564     strip.className = "x-tabs-wrap";
11565     container.appendChild(strip);
11566     return strip;
11567 };
11568 /** @private */
11569 Roo.TabPanel.prototype.createStripList = function(strip){
11570     // div wrapper for retard IE
11571     // returns the "tr" element.
11572     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
11573         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
11574         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
11575     return strip.firstChild.firstChild.firstChild.firstChild;
11576 };
11577 /** @private */
11578 Roo.TabPanel.prototype.createBody = function(container){
11579     var body = document.createElement("div");
11580     Roo.id(body, "tab-body");
11581     Roo.fly(body).addClass("x-tabs-body");
11582     container.appendChild(body);
11583     return body;
11584 };
11585 /** @private */
11586 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11587     var body = Roo.getDom(id);
11588     if(!body){
11589         body = document.createElement("div");
11590         body.id = id;
11591     }
11592     Roo.fly(body).addClass("x-tabs-item-body");
11593     bodyEl.insertBefore(body, bodyEl.firstChild);
11594     return body;
11595 };
11596 /** @private */
11597 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11598     var td = document.createElement("td");
11599     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
11600     //stripEl.appendChild(td);
11601     if(closable){
11602         td.className = "x-tabs-closable";
11603         if(!this.closeTpl){
11604             this.closeTpl = new Roo.Template(
11605                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11606                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11607                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11608             );
11609         }
11610         var el = this.closeTpl.overwrite(td, {"text": text});
11611         var close = el.getElementsByTagName("div")[0];
11612         var inner = el.getElementsByTagName("em")[0];
11613         return {"el": el, "close": close, "inner": inner};
11614     } else {
11615         if(!this.tabTpl){
11616             this.tabTpl = new Roo.Template(
11617                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11618                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11619             );
11620         }
11621         var el = this.tabTpl.overwrite(td, {"text": text});
11622         var inner = el.getElementsByTagName("em")[0];
11623         return {"el": el, "inner": inner};
11624     }
11625 };/*
11626  * Based on:
11627  * Ext JS Library 1.1.1
11628  * Copyright(c) 2006-2007, Ext JS, LLC.
11629  *
11630  * Originally Released Under LGPL - original licence link has changed is not relivant.
11631  *
11632  * Fork - LGPL
11633  * <script type="text/javascript">
11634  */
11635
11636 /**
11637  * @class Roo.Button
11638  * @extends Roo.util.Observable
11639  * Simple Button class
11640  * @cfg {String} text The button text
11641  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11642  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11643  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11644  * @cfg {Object} scope The scope of the handler
11645  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11646  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11647  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11648  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11649  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11650  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11651    applies if enableToggle = true)
11652  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11653  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11654   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11655  * @constructor
11656  * Create a new button
11657  * @param {Object} config The config object
11658  */
11659 Roo.Button = function(renderTo, config)
11660 {
11661     if (!config) {
11662         config = renderTo;
11663         renderTo = config.renderTo || false;
11664     }
11665     
11666     Roo.apply(this, config);
11667     this.addEvents({
11668         /**
11669              * @event click
11670              * Fires when this button is clicked
11671              * @param {Button} this
11672              * @param {EventObject} e The click event
11673              */
11674             "click" : true,
11675         /**
11676              * @event toggle
11677              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11678              * @param {Button} this
11679              * @param {Boolean} pressed
11680              */
11681             "toggle" : true,
11682         /**
11683              * @event mouseover
11684              * Fires when the mouse hovers over the button
11685              * @param {Button} this
11686              * @param {Event} e The event object
11687              */
11688         'mouseover' : true,
11689         /**
11690              * @event mouseout
11691              * Fires when the mouse exits the button
11692              * @param {Button} this
11693              * @param {Event} e The event object
11694              */
11695         'mouseout': true,
11696          /**
11697              * @event render
11698              * Fires when the button is rendered
11699              * @param {Button} this
11700              */
11701         'render': true
11702     });
11703     if(this.menu){
11704         this.menu = Roo.menu.MenuMgr.get(this.menu);
11705     }
11706     // register listeners first!!  - so render can be captured..
11707     Roo.util.Observable.call(this);
11708     if(renderTo){
11709         this.render(renderTo);
11710     }
11711     
11712   
11713 };
11714
11715 Roo.extend(Roo.Button, Roo.util.Observable, {
11716     /**
11717      * 
11718      */
11719     
11720     /**
11721      * Read-only. True if this button is hidden
11722      * @type Boolean
11723      */
11724     hidden : false,
11725     /**
11726      * Read-only. True if this button is disabled
11727      * @type Boolean
11728      */
11729     disabled : false,
11730     /**
11731      * Read-only. True if this button is pressed (only if enableToggle = true)
11732      * @type Boolean
11733      */
11734     pressed : false,
11735
11736     /**
11737      * @cfg {Number} tabIndex 
11738      * The DOM tabIndex for this button (defaults to undefined)
11739      */
11740     tabIndex : undefined,
11741
11742     /**
11743      * @cfg {Boolean} enableToggle
11744      * True to enable pressed/not pressed toggling (defaults to false)
11745      */
11746     enableToggle: false,
11747     /**
11748      * @cfg {Mixed} menu
11749      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11750      */
11751     menu : undefined,
11752     /**
11753      * @cfg {String} menuAlign
11754      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11755      */
11756     menuAlign : "tl-bl?",
11757
11758     /**
11759      * @cfg {String} iconCls
11760      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11761      */
11762     iconCls : undefined,
11763     /**
11764      * @cfg {String} type
11765      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11766      */
11767     type : 'button',
11768
11769     // private
11770     menuClassTarget: 'tr',
11771
11772     /**
11773      * @cfg {String} clickEvent
11774      * The type of event to map to the button's event handler (defaults to 'click')
11775      */
11776     clickEvent : 'click',
11777
11778     /**
11779      * @cfg {Boolean} handleMouseEvents
11780      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11781      */
11782     handleMouseEvents : true,
11783
11784     /**
11785      * @cfg {String} tooltipType
11786      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11787      */
11788     tooltipType : 'qtip',
11789
11790     /**
11791      * @cfg {String} cls
11792      * A CSS class to apply to the button's main element.
11793      */
11794     
11795     /**
11796      * @cfg {Roo.Template} template (Optional)
11797      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11798      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11799      * require code modifications if required elements (e.g. a button) aren't present.
11800      */
11801
11802     // private
11803     render : function(renderTo){
11804         var btn;
11805         if(this.hideParent){
11806             this.parentEl = Roo.get(renderTo);
11807         }
11808         if(!this.dhconfig){
11809             if(!this.template){
11810                 if(!Roo.Button.buttonTemplate){
11811                     // hideous table template
11812                     Roo.Button.buttonTemplate = new Roo.Template(
11813                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11814                         '<td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><em unselectable="on"><button class="x-btn-text" type="{1}">{0}</button></em></td><td class="x-btn-right"><i>&#160;</i></td>',
11815                         "</tr></tbody></table>");
11816                 }
11817                 this.template = Roo.Button.buttonTemplate;
11818             }
11819             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11820             var btnEl = btn.child("button:first");
11821             btnEl.on('focus', this.onFocus, this);
11822             btnEl.on('blur', this.onBlur, this);
11823             if(this.cls){
11824                 btn.addClass(this.cls);
11825             }
11826             if(this.icon){
11827                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11828             }
11829             if(this.iconCls){
11830                 btnEl.addClass(this.iconCls);
11831                 if(!this.cls){
11832                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11833                 }
11834             }
11835             if(this.tabIndex !== undefined){
11836                 btnEl.dom.tabIndex = this.tabIndex;
11837             }
11838             if(this.tooltip){
11839                 if(typeof this.tooltip == 'object'){
11840                     Roo.QuickTips.tips(Roo.apply({
11841                           target: btnEl.id
11842                     }, this.tooltip));
11843                 } else {
11844                     btnEl.dom[this.tooltipType] = this.tooltip;
11845                 }
11846             }
11847         }else{
11848             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11849         }
11850         this.el = btn;
11851         if(this.id){
11852             this.el.dom.id = this.el.id = this.id;
11853         }
11854         if(this.menu){
11855             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11856             this.menu.on("show", this.onMenuShow, this);
11857             this.menu.on("hide", this.onMenuHide, this);
11858         }
11859         btn.addClass("x-btn");
11860         if(Roo.isIE && !Roo.isIE7){
11861             this.autoWidth.defer(1, this);
11862         }else{
11863             this.autoWidth();
11864         }
11865         if(this.handleMouseEvents){
11866             btn.on("mouseover", this.onMouseOver, this);
11867             btn.on("mouseout", this.onMouseOut, this);
11868             btn.on("mousedown", this.onMouseDown, this);
11869         }
11870         btn.on(this.clickEvent, this.onClick, this);
11871         //btn.on("mouseup", this.onMouseUp, this);
11872         if(this.hidden){
11873             this.hide();
11874         }
11875         if(this.disabled){
11876             this.disable();
11877         }
11878         Roo.ButtonToggleMgr.register(this);
11879         if(this.pressed){
11880             this.el.addClass("x-btn-pressed");
11881         }
11882         if(this.repeat){
11883             var repeater = new Roo.util.ClickRepeater(btn,
11884                 typeof this.repeat == "object" ? this.repeat : {}
11885             );
11886             repeater.on("click", this.onClick,  this);
11887         }
11888         
11889         this.fireEvent('render', this);
11890         
11891     },
11892     /**
11893      * Returns the button's underlying element
11894      * @return {Roo.Element} The element
11895      */
11896     getEl : function(){
11897         return this.el;  
11898     },
11899     
11900     /**
11901      * Destroys this Button and removes any listeners.
11902      */
11903     destroy : function(){
11904         Roo.ButtonToggleMgr.unregister(this);
11905         this.el.removeAllListeners();
11906         this.purgeListeners();
11907         this.el.remove();
11908     },
11909
11910     // private
11911     autoWidth : function(){
11912         if(this.el){
11913             this.el.setWidth("auto");
11914             if(Roo.isIE7 && Roo.isStrict){
11915                 var ib = this.el.child('button');
11916                 if(ib && ib.getWidth() > 20){
11917                     ib.clip();
11918                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11919                 }
11920             }
11921             if(this.minWidth){
11922                 if(this.hidden){
11923                     this.el.beginMeasure();
11924                 }
11925                 if(this.el.getWidth() < this.minWidth){
11926                     this.el.setWidth(this.minWidth);
11927                 }
11928                 if(this.hidden){
11929                     this.el.endMeasure();
11930                 }
11931             }
11932         }
11933     },
11934
11935     /**
11936      * Assigns this button's click handler
11937      * @param {Function} handler The function to call when the button is clicked
11938      * @param {Object} scope (optional) Scope for the function passed in
11939      */
11940     setHandler : function(handler, scope){
11941         this.handler = handler;
11942         this.scope = scope;  
11943     },
11944     
11945     /**
11946      * Sets this button's text
11947      * @param {String} text The button text
11948      */
11949     setText : function(text){
11950         this.text = text;
11951         if(this.el){
11952             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11953         }
11954         this.autoWidth();
11955     },
11956     
11957     /**
11958      * Gets the text for this button
11959      * @return {String} The button text
11960      */
11961     getText : function(){
11962         return this.text;  
11963     },
11964     
11965     /**
11966      * Show this button
11967      */
11968     show: function(){
11969         this.hidden = false;
11970         if(this.el){
11971             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11972         }
11973     },
11974     
11975     /**
11976      * Hide this button
11977      */
11978     hide: function(){
11979         this.hidden = true;
11980         if(this.el){
11981             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11982         }
11983     },
11984     
11985     /**
11986      * Convenience function for boolean show/hide
11987      * @param {Boolean} visible True to show, false to hide
11988      */
11989     setVisible: function(visible){
11990         if(visible) {
11991             this.show();
11992         }else{
11993             this.hide();
11994         }
11995     },
11996     
11997     /**
11998      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11999      * @param {Boolean} state (optional) Force a particular state
12000      */
12001     toggle : function(state){
12002         state = state === undefined ? !this.pressed : state;
12003         if(state != this.pressed){
12004             if(state){
12005                 this.el.addClass("x-btn-pressed");
12006                 this.pressed = true;
12007                 this.fireEvent("toggle", this, true);
12008             }else{
12009                 this.el.removeClass("x-btn-pressed");
12010                 this.pressed = false;
12011                 this.fireEvent("toggle", this, false);
12012             }
12013             if(this.toggleHandler){
12014                 this.toggleHandler.call(this.scope || this, this, state);
12015             }
12016         }
12017     },
12018     
12019     /**
12020      * Focus the button
12021      */
12022     focus : function(){
12023         this.el.child('button:first').focus();
12024     },
12025     
12026     /**
12027      * Disable this button
12028      */
12029     disable : function(){
12030         if(this.el){
12031             this.el.addClass("x-btn-disabled");
12032         }
12033         this.disabled = true;
12034     },
12035     
12036     /**
12037      * Enable this button
12038      */
12039     enable : function(){
12040         if(this.el){
12041             this.el.removeClass("x-btn-disabled");
12042         }
12043         this.disabled = false;
12044     },
12045
12046     /**
12047      * Convenience function for boolean enable/disable
12048      * @param {Boolean} enabled True to enable, false to disable
12049      */
12050     setDisabled : function(v){
12051         this[v !== true ? "enable" : "disable"]();
12052     },
12053
12054     // private
12055     onClick : function(e){
12056         if(e){
12057             e.preventDefault();
12058         }
12059         if(e.button != 0){
12060             return;
12061         }
12062         if(!this.disabled){
12063             if(this.enableToggle){
12064                 this.toggle();
12065             }
12066             if(this.menu && !this.menu.isVisible()){
12067                 this.menu.show(this.el, this.menuAlign);
12068             }
12069             this.fireEvent("click", this, e);
12070             if(this.handler){
12071                 this.el.removeClass("x-btn-over");
12072                 this.handler.call(this.scope || this, this, e);
12073             }
12074         }
12075     },
12076     // private
12077     onMouseOver : function(e){
12078         if(!this.disabled){
12079             this.el.addClass("x-btn-over");
12080             this.fireEvent('mouseover', this, e);
12081         }
12082     },
12083     // private
12084     onMouseOut : function(e){
12085         if(!e.within(this.el,  true)){
12086             this.el.removeClass("x-btn-over");
12087             this.fireEvent('mouseout', this, e);
12088         }
12089     },
12090     // private
12091     onFocus : function(e){
12092         if(!this.disabled){
12093             this.el.addClass("x-btn-focus");
12094         }
12095     },
12096     // private
12097     onBlur : function(e){
12098         this.el.removeClass("x-btn-focus");
12099     },
12100     // private
12101     onMouseDown : function(e){
12102         if(!this.disabled && e.button == 0){
12103             this.el.addClass("x-btn-click");
12104             Roo.get(document).on('mouseup', this.onMouseUp, this);
12105         }
12106     },
12107     // private
12108     onMouseUp : function(e){
12109         if(e.button == 0){
12110             this.el.removeClass("x-btn-click");
12111             Roo.get(document).un('mouseup', this.onMouseUp, this);
12112         }
12113     },
12114     // private
12115     onMenuShow : function(e){
12116         this.el.addClass("x-btn-menu-active");
12117     },
12118     // private
12119     onMenuHide : function(e){
12120         this.el.removeClass("x-btn-menu-active");
12121     }   
12122 });
12123
12124 // Private utility class used by Button
12125 Roo.ButtonToggleMgr = function(){
12126    var groups = {};
12127    
12128    function toggleGroup(btn, state){
12129        if(state){
12130            var g = groups[btn.toggleGroup];
12131            for(var i = 0, l = g.length; i < l; i++){
12132                if(g[i] != btn){
12133                    g[i].toggle(false);
12134                }
12135            }
12136        }
12137    }
12138    
12139    return {
12140        register : function(btn){
12141            if(!btn.toggleGroup){
12142                return;
12143            }
12144            var g = groups[btn.toggleGroup];
12145            if(!g){
12146                g = groups[btn.toggleGroup] = [];
12147            }
12148            g.push(btn);
12149            btn.on("toggle", toggleGroup);
12150        },
12151        
12152        unregister : function(btn){
12153            if(!btn.toggleGroup){
12154                return;
12155            }
12156            var g = groups[btn.toggleGroup];
12157            if(g){
12158                g.remove(btn);
12159                btn.un("toggle", toggleGroup);
12160            }
12161        }
12162    };
12163 }();/*
12164  * Based on:
12165  * Ext JS Library 1.1.1
12166  * Copyright(c) 2006-2007, Ext JS, LLC.
12167  *
12168  * Originally Released Under LGPL - original licence link has changed is not relivant.
12169  *
12170  * Fork - LGPL
12171  * <script type="text/javascript">
12172  */
12173  
12174 /**
12175  * @class Roo.SplitButton
12176  * @extends Roo.Button
12177  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
12178  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
12179  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
12180  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
12181  * @cfg {String} arrowTooltip The title attribute of the arrow
12182  * @constructor
12183  * Create a new menu button
12184  * @param {String/HTMLElement/Element} renderTo The element to append the button to
12185  * @param {Object} config The config object
12186  */
12187 Roo.SplitButton = function(renderTo, config){
12188     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
12189     /**
12190      * @event arrowclick
12191      * Fires when this button's arrow is clicked
12192      * @param {SplitButton} this
12193      * @param {EventObject} e The click event
12194      */
12195     this.addEvents({"arrowclick":true});
12196 };
12197
12198 Roo.extend(Roo.SplitButton, Roo.Button, {
12199     render : function(renderTo){
12200         // this is one sweet looking template!
12201         var tpl = new Roo.Template(
12202             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
12203             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
12204             '<tr><td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><button class="x-btn-text" type="{1}">{0}</button></td></tr>',
12205             "</tbody></table></td><td>",
12206             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
12207             '<tr><td class="x-btn-center"><button class="x-btn-menu-arrow-el" type="button">&#160;</button></td><td class="x-btn-right"><i>&#160;</i></td></tr>',
12208             "</tbody></table></td></tr></table>"
12209         );
12210         var btn = tpl.append(renderTo, [this.text, this.type], true);
12211         var btnEl = btn.child("button");
12212         if(this.cls){
12213             btn.addClass(this.cls);
12214         }
12215         if(this.icon){
12216             btnEl.setStyle('background-image', 'url(' +this.icon +')');
12217         }
12218         if(this.iconCls){
12219             btnEl.addClass(this.iconCls);
12220             if(!this.cls){
12221                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
12222             }
12223         }
12224         this.el = btn;
12225         if(this.handleMouseEvents){
12226             btn.on("mouseover", this.onMouseOver, this);
12227             btn.on("mouseout", this.onMouseOut, this);
12228             btn.on("mousedown", this.onMouseDown, this);
12229             btn.on("mouseup", this.onMouseUp, this);
12230         }
12231         btn.on(this.clickEvent, this.onClick, this);
12232         if(this.tooltip){
12233             if(typeof this.tooltip == 'object'){
12234                 Roo.QuickTips.tips(Roo.apply({
12235                       target: btnEl.id
12236                 }, this.tooltip));
12237             } else {
12238                 btnEl.dom[this.tooltipType] = this.tooltip;
12239             }
12240         }
12241         if(this.arrowTooltip){
12242             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
12243         }
12244         if(this.hidden){
12245             this.hide();
12246         }
12247         if(this.disabled){
12248             this.disable();
12249         }
12250         if(this.pressed){
12251             this.el.addClass("x-btn-pressed");
12252         }
12253         if(Roo.isIE && !Roo.isIE7){
12254             this.autoWidth.defer(1, this);
12255         }else{
12256             this.autoWidth();
12257         }
12258         if(this.menu){
12259             this.menu.on("show", this.onMenuShow, this);
12260             this.menu.on("hide", this.onMenuHide, this);
12261         }
12262         this.fireEvent('render', this);
12263     },
12264
12265     // private
12266     autoWidth : function(){
12267         if(this.el){
12268             var tbl = this.el.child("table:first");
12269             var tbl2 = this.el.child("table:last");
12270             this.el.setWidth("auto");
12271             tbl.setWidth("auto");
12272             if(Roo.isIE7 && Roo.isStrict){
12273                 var ib = this.el.child('button:first');
12274                 if(ib && ib.getWidth() > 20){
12275                     ib.clip();
12276                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12277                 }
12278             }
12279             if(this.minWidth){
12280                 if(this.hidden){
12281                     this.el.beginMeasure();
12282                 }
12283                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12284                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12285                 }
12286                 if(this.hidden){
12287                     this.el.endMeasure();
12288                 }
12289             }
12290             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12291         } 
12292     },
12293     /**
12294      * Sets this button's click handler
12295      * @param {Function} handler The function to call when the button is clicked
12296      * @param {Object} scope (optional) Scope for the function passed above
12297      */
12298     setHandler : function(handler, scope){
12299         this.handler = handler;
12300         this.scope = scope;  
12301     },
12302     
12303     /**
12304      * Sets this button's arrow click handler
12305      * @param {Function} handler The function to call when the arrow is clicked
12306      * @param {Object} scope (optional) Scope for the function passed above
12307      */
12308     setArrowHandler : function(handler, scope){
12309         this.arrowHandler = handler;
12310         this.scope = scope;  
12311     },
12312     
12313     /**
12314      * Focus the button
12315      */
12316     focus : function(){
12317         if(this.el){
12318             this.el.child("button:first").focus();
12319         }
12320     },
12321
12322     // private
12323     onClick : function(e){
12324         e.preventDefault();
12325         if(!this.disabled){
12326             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12327                 if(this.menu && !this.menu.isVisible()){
12328                     this.menu.show(this.el, this.menuAlign);
12329                 }
12330                 this.fireEvent("arrowclick", this, e);
12331                 if(this.arrowHandler){
12332                     this.arrowHandler.call(this.scope || this, this, e);
12333                 }
12334             }else{
12335                 this.fireEvent("click", this, e);
12336                 if(this.handler){
12337                     this.handler.call(this.scope || this, this, e);
12338                 }
12339             }
12340         }
12341     },
12342     // private
12343     onMouseDown : function(e){
12344         if(!this.disabled){
12345             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12346         }
12347     },
12348     // private
12349     onMouseUp : function(e){
12350         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12351     }   
12352 });
12353
12354
12355 // backwards compat
12356 Roo.MenuButton = Roo.SplitButton;/*
12357  * Based on:
12358  * Ext JS Library 1.1.1
12359  * Copyright(c) 2006-2007, Ext JS, LLC.
12360  *
12361  * Originally Released Under LGPL - original licence link has changed is not relivant.
12362  *
12363  * Fork - LGPL
12364  * <script type="text/javascript">
12365  */
12366
12367 /**
12368  * @class Roo.Toolbar
12369  * Basic Toolbar class.
12370  * @constructor
12371  * Creates a new Toolbar
12372  * @param {Object} container The config object
12373  */ 
12374 Roo.Toolbar = function(container, buttons, config)
12375 {
12376     /// old consturctor format still supported..
12377     if(container instanceof Array){ // omit the container for later rendering
12378         buttons = container;
12379         config = buttons;
12380         container = null;
12381     }
12382     if (typeof(container) == 'object' && container.xtype) {
12383         config = container;
12384         container = config.container;
12385         buttons = config.buttons || []; // not really - use items!!
12386     }
12387     var xitems = [];
12388     if (config && config.items) {
12389         xitems = config.items;
12390         delete config.items;
12391     }
12392     Roo.apply(this, config);
12393     this.buttons = buttons;
12394     
12395     if(container){
12396         this.render(container);
12397     }
12398     this.xitems = xitems;
12399     Roo.each(xitems, function(b) {
12400         this.add(b);
12401     }, this);
12402     
12403 };
12404
12405 Roo.Toolbar.prototype = {
12406     /**
12407      * @cfg {Array} items
12408      * array of button configs or elements to add (will be converted to a MixedCollection)
12409      */
12410     
12411     /**
12412      * @cfg {String/HTMLElement/Element} container
12413      * The id or element that will contain the toolbar
12414      */
12415     // private
12416     render : function(ct){
12417         this.el = Roo.get(ct);
12418         if(this.cls){
12419             this.el.addClass(this.cls);
12420         }
12421         // using a table allows for vertical alignment
12422         // 100% width is needed by Safari...
12423         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12424         this.tr = this.el.child("tr", true);
12425         var autoId = 0;
12426         this.items = new Roo.util.MixedCollection(false, function(o){
12427             return o.id || ("item" + (++autoId));
12428         });
12429         if(this.buttons){
12430             this.add.apply(this, this.buttons);
12431             delete this.buttons;
12432         }
12433     },
12434
12435     /**
12436      * Adds element(s) to the toolbar -- this function takes a variable number of 
12437      * arguments of mixed type and adds them to the toolbar.
12438      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12439      * <ul>
12440      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12441      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12442      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12443      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12444      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12445      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12446      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12447      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12448      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12449      * </ul>
12450      * @param {Mixed} arg2
12451      * @param {Mixed} etc.
12452      */
12453     add : function(){
12454         var a = arguments, l = a.length;
12455         for(var i = 0; i < l; i++){
12456             this._add(a[i]);
12457         }
12458     },
12459     // private..
12460     _add : function(el) {
12461         
12462         if (el.xtype) {
12463             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12464         }
12465         
12466         if (el.applyTo){ // some kind of form field
12467             return this.addField(el);
12468         } 
12469         if (el.render){ // some kind of Toolbar.Item
12470             return this.addItem(el);
12471         }
12472         if (typeof el == "string"){ // string
12473             if(el == "separator" || el == "-"){
12474                 return this.addSeparator();
12475             }
12476             if (el == " "){
12477                 return this.addSpacer();
12478             }
12479             if(el == "->"){
12480                 return this.addFill();
12481             }
12482             return this.addText(el);
12483             
12484         }
12485         if(el.tagName){ // element
12486             return this.addElement(el);
12487         }
12488         if(typeof el == "object"){ // must be button config?
12489             return this.addButton(el);
12490         }
12491         // and now what?!?!
12492         return false;
12493         
12494     },
12495     
12496     /**
12497      * Add an Xtype element
12498      * @param {Object} xtype Xtype Object
12499      * @return {Object} created Object
12500      */
12501     addxtype : function(e){
12502         return this.add(e);  
12503     },
12504     
12505     /**
12506      * Returns the Element for this toolbar.
12507      * @return {Roo.Element}
12508      */
12509     getEl : function(){
12510         return this.el;  
12511     },
12512     
12513     /**
12514      * Adds a separator
12515      * @return {Roo.Toolbar.Item} The separator item
12516      */
12517     addSeparator : function(){
12518         return this.addItem(new Roo.Toolbar.Separator());
12519     },
12520
12521     /**
12522      * Adds a spacer element
12523      * @return {Roo.Toolbar.Spacer} The spacer item
12524      */
12525     addSpacer : function(){
12526         return this.addItem(new Roo.Toolbar.Spacer());
12527     },
12528
12529     /**
12530      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12531      * @return {Roo.Toolbar.Fill} The fill item
12532      */
12533     addFill : function(){
12534         return this.addItem(new Roo.Toolbar.Fill());
12535     },
12536
12537     /**
12538      * Adds any standard HTML element to the toolbar
12539      * @param {String/HTMLElement/Element} el The element or id of the element to add
12540      * @return {Roo.Toolbar.Item} The element's item
12541      */
12542     addElement : function(el){
12543         return this.addItem(new Roo.Toolbar.Item(el));
12544     },
12545     /**
12546      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12547      * @type Roo.util.MixedCollection  
12548      */
12549     items : false,
12550      
12551     /**
12552      * Adds any Toolbar.Item or subclass
12553      * @param {Roo.Toolbar.Item} item
12554      * @return {Roo.Toolbar.Item} The item
12555      */
12556     addItem : function(item){
12557         var td = this.nextBlock();
12558         item.render(td);
12559         this.items.add(item);
12560         return item;
12561     },
12562     
12563     /**
12564      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12565      * @param {Object/Array} config A button config or array of configs
12566      * @return {Roo.Toolbar.Button/Array}
12567      */
12568     addButton : function(config){
12569         if(config instanceof Array){
12570             var buttons = [];
12571             for(var i = 0, len = config.length; i < len; i++) {
12572                 buttons.push(this.addButton(config[i]));
12573             }
12574             return buttons;
12575         }
12576         var b = config;
12577         if(!(config instanceof Roo.Toolbar.Button)){
12578             b = config.split ?
12579                 new Roo.Toolbar.SplitButton(config) :
12580                 new Roo.Toolbar.Button(config);
12581         }
12582         var td = this.nextBlock();
12583         b.render(td);
12584         this.items.add(b);
12585         return b;
12586     },
12587     
12588     /**
12589      * Adds text to the toolbar
12590      * @param {String} text The text to add
12591      * @return {Roo.Toolbar.Item} The element's item
12592      */
12593     addText : function(text){
12594         return this.addItem(new Roo.Toolbar.TextItem(text));
12595     },
12596     
12597     /**
12598      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12599      * @param {Number} index The index where the item is to be inserted
12600      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12601      * @return {Roo.Toolbar.Button/Item}
12602      */
12603     insertButton : function(index, item){
12604         if(item instanceof Array){
12605             var buttons = [];
12606             for(var i = 0, len = item.length; i < len; i++) {
12607                buttons.push(this.insertButton(index + i, item[i]));
12608             }
12609             return buttons;
12610         }
12611         if (!(item instanceof Roo.Toolbar.Button)){
12612            item = new Roo.Toolbar.Button(item);
12613         }
12614         var td = document.createElement("td");
12615         this.tr.insertBefore(td, this.tr.childNodes[index]);
12616         item.render(td);
12617         this.items.insert(index, item);
12618         return item;
12619     },
12620     
12621     /**
12622      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12623      * @param {Object} config
12624      * @return {Roo.Toolbar.Item} The element's item
12625      */
12626     addDom : function(config, returnEl){
12627         var td = this.nextBlock();
12628         Roo.DomHelper.overwrite(td, config);
12629         var ti = new Roo.Toolbar.Item(td.firstChild);
12630         ti.render(td);
12631         this.items.add(ti);
12632         return ti;
12633     },
12634
12635     /**
12636      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12637      * @type Roo.util.MixedCollection  
12638      */
12639     fields : false,
12640     
12641     /**
12642      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
12643      * Note: the field should not have been rendered yet. For a field that has already been
12644      * rendered, use {@link #addElement}.
12645      * @param {Roo.form.Field} field
12646      * @return {Roo.ToolbarItem}
12647      */
12648      
12649       
12650     addField : function(field) {
12651         if (!this.fields) {
12652             var autoId = 0;
12653             this.fields = new Roo.util.MixedCollection(false, function(o){
12654                 return o.id || ("item" + (++autoId));
12655             });
12656
12657         }
12658         
12659         var td = this.nextBlock();
12660         field.render(td);
12661         var ti = new Roo.Toolbar.Item(td.firstChild);
12662         ti.render(td);
12663         this.items.add(ti);
12664         this.fields.add(field);
12665         return ti;
12666     },
12667     /**
12668      * Hide the toolbar
12669      * @method hide
12670      */
12671      
12672       
12673     hide : function()
12674     {
12675         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12676         this.el.child('div').hide();
12677     },
12678     /**
12679      * Show the toolbar
12680      * @method show
12681      */
12682     show : function()
12683     {
12684         this.el.child('div').show();
12685     },
12686       
12687     // private
12688     nextBlock : function(){
12689         var td = document.createElement("td");
12690         this.tr.appendChild(td);
12691         return td;
12692     },
12693
12694     // private
12695     destroy : function(){
12696         if(this.items){ // rendered?
12697             Roo.destroy.apply(Roo, this.items.items);
12698         }
12699         if(this.fields){ // rendered?
12700             Roo.destroy.apply(Roo, this.fields.items);
12701         }
12702         Roo.Element.uncache(this.el, this.tr);
12703     }
12704 };
12705
12706 /**
12707  * @class Roo.Toolbar.Item
12708  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12709  * @constructor
12710  * Creates a new Item
12711  * @param {HTMLElement} el 
12712  */
12713 Roo.Toolbar.Item = function(el){
12714     this.el = Roo.getDom(el);
12715     this.id = Roo.id(this.el);
12716     this.hidden = false;
12717 };
12718
12719 Roo.Toolbar.Item.prototype = {
12720     
12721     /**
12722      * Get this item's HTML Element
12723      * @return {HTMLElement}
12724      */
12725     getEl : function(){
12726        return this.el;  
12727     },
12728
12729     // private
12730     render : function(td){
12731         this.td = td;
12732         td.appendChild(this.el);
12733     },
12734     
12735     /**
12736      * Removes and destroys this item.
12737      */
12738     destroy : function(){
12739         this.td.parentNode.removeChild(this.td);
12740     },
12741     
12742     /**
12743      * Shows this item.
12744      */
12745     show: function(){
12746         this.hidden = false;
12747         this.td.style.display = "";
12748     },
12749     
12750     /**
12751      * Hides this item.
12752      */
12753     hide: function(){
12754         this.hidden = true;
12755         this.td.style.display = "none";
12756     },
12757     
12758     /**
12759      * Convenience function for boolean show/hide.
12760      * @param {Boolean} visible true to show/false to hide
12761      */
12762     setVisible: function(visible){
12763         if(visible) {
12764             this.show();
12765         }else{
12766             this.hide();
12767         }
12768     },
12769     
12770     /**
12771      * Try to focus this item.
12772      */
12773     focus : function(){
12774         Roo.fly(this.el).focus();
12775     },
12776     
12777     /**
12778      * Disables this item.
12779      */
12780     disable : function(){
12781         Roo.fly(this.td).addClass("x-item-disabled");
12782         this.disabled = true;
12783         this.el.disabled = true;
12784     },
12785     
12786     /**
12787      * Enables this item.
12788      */
12789     enable : function(){
12790         Roo.fly(this.td).removeClass("x-item-disabled");
12791         this.disabled = false;
12792         this.el.disabled = false;
12793     }
12794 };
12795
12796
12797 /**
12798  * @class Roo.Toolbar.Separator
12799  * @extends Roo.Toolbar.Item
12800  * A simple toolbar separator class
12801  * @constructor
12802  * Creates a new Separator
12803  */
12804 Roo.Toolbar.Separator = function(){
12805     var s = document.createElement("span");
12806     s.className = "ytb-sep";
12807     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12808 };
12809 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12810     enable:Roo.emptyFn,
12811     disable:Roo.emptyFn,
12812     focus:Roo.emptyFn
12813 });
12814
12815 /**
12816  * @class Roo.Toolbar.Spacer
12817  * @extends Roo.Toolbar.Item
12818  * A simple element that adds extra horizontal space to a toolbar.
12819  * @constructor
12820  * Creates a new Spacer
12821  */
12822 Roo.Toolbar.Spacer = function(){
12823     var s = document.createElement("div");
12824     s.className = "ytb-spacer";
12825     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12826 };
12827 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12828     enable:Roo.emptyFn,
12829     disable:Roo.emptyFn,
12830     focus:Roo.emptyFn
12831 });
12832
12833 /**
12834  * @class Roo.Toolbar.Fill
12835  * @extends Roo.Toolbar.Spacer
12836  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12837  * @constructor
12838  * Creates a new Spacer
12839  */
12840 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12841     // private
12842     render : function(td){
12843         td.style.width = '100%';
12844         Roo.Toolbar.Fill.superclass.render.call(this, td);
12845     }
12846 });
12847
12848 /**
12849  * @class Roo.Toolbar.TextItem
12850  * @extends Roo.Toolbar.Item
12851  * A simple class that renders text directly into a toolbar.
12852  * @constructor
12853  * Creates a new TextItem
12854  * @param {String} text
12855  */
12856 Roo.Toolbar.TextItem = function(text){
12857     if (typeof(text) == 'object') {
12858         text = text.text;
12859     }
12860     var s = document.createElement("span");
12861     s.className = "ytb-text";
12862     s.innerHTML = text;
12863     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12864 };
12865 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12866     enable:Roo.emptyFn,
12867     disable:Roo.emptyFn,
12868     focus:Roo.emptyFn
12869 });
12870
12871 /**
12872  * @class Roo.Toolbar.Button
12873  * @extends Roo.Button
12874  * A button that renders into a toolbar.
12875  * @constructor
12876  * Creates a new Button
12877  * @param {Object} config A standard {@link Roo.Button} config object
12878  */
12879 Roo.Toolbar.Button = function(config){
12880     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12881 };
12882 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12883     render : function(td){
12884         this.td = td;
12885         Roo.Toolbar.Button.superclass.render.call(this, td);
12886     },
12887     
12888     /**
12889      * Removes and destroys this button
12890      */
12891     destroy : function(){
12892         Roo.Toolbar.Button.superclass.destroy.call(this);
12893         this.td.parentNode.removeChild(this.td);
12894     },
12895     
12896     /**
12897      * Shows this button
12898      */
12899     show: function(){
12900         this.hidden = false;
12901         this.td.style.display = "";
12902     },
12903     
12904     /**
12905      * Hides this button
12906      */
12907     hide: function(){
12908         this.hidden = true;
12909         this.td.style.display = "none";
12910     },
12911
12912     /**
12913      * Disables this item
12914      */
12915     disable : function(){
12916         Roo.fly(this.td).addClass("x-item-disabled");
12917         this.disabled = true;
12918     },
12919
12920     /**
12921      * Enables this item
12922      */
12923     enable : function(){
12924         Roo.fly(this.td).removeClass("x-item-disabled");
12925         this.disabled = false;
12926     }
12927 });
12928 // backwards compat
12929 Roo.ToolbarButton = Roo.Toolbar.Button;
12930
12931 /**
12932  * @class Roo.Toolbar.SplitButton
12933  * @extends Roo.SplitButton
12934  * A menu button that renders into a toolbar.
12935  * @constructor
12936  * Creates a new SplitButton
12937  * @param {Object} config A standard {@link Roo.SplitButton} config object
12938  */
12939 Roo.Toolbar.SplitButton = function(config){
12940     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12941 };
12942 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12943     render : function(td){
12944         this.td = td;
12945         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12946     },
12947     
12948     /**
12949      * Removes and destroys this button
12950      */
12951     destroy : function(){
12952         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12953         this.td.parentNode.removeChild(this.td);
12954     },
12955     
12956     /**
12957      * Shows this button
12958      */
12959     show: function(){
12960         this.hidden = false;
12961         this.td.style.display = "";
12962     },
12963     
12964     /**
12965      * Hides this button
12966      */
12967     hide: function(){
12968         this.hidden = true;
12969         this.td.style.display = "none";
12970     }
12971 });
12972
12973 // backwards compat
12974 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12975  * Based on:
12976  * Ext JS Library 1.1.1
12977  * Copyright(c) 2006-2007, Ext JS, LLC.
12978  *
12979  * Originally Released Under LGPL - original licence link has changed is not relivant.
12980  *
12981  * Fork - LGPL
12982  * <script type="text/javascript">
12983  */
12984  
12985 /**
12986  * @class Roo.PagingToolbar
12987  * @extends Roo.Toolbar
12988  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12989  * @constructor
12990  * Create a new PagingToolbar
12991  * @param {Object} config The config object
12992  */
12993 Roo.PagingToolbar = function(el, ds, config)
12994 {
12995     // old args format still supported... - xtype is prefered..
12996     if (typeof(el) == 'object' && el.xtype) {
12997         // created from xtype...
12998         config = el;
12999         ds = el.dataSource;
13000         el = config.container;
13001     }
13002     var items = [];
13003     if (config.items) {
13004         items = config.items;
13005         config.items = [];
13006     }
13007     
13008     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
13009     this.ds = ds;
13010     this.cursor = 0;
13011     this.renderButtons(this.el);
13012     this.bind(ds);
13013     
13014     // supprot items array.
13015    
13016     Roo.each(items, function(e) {
13017         this.add(Roo.factory(e));
13018     },this);
13019     
13020 };
13021
13022 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
13023     /**
13024      * @cfg {Roo.data.Store} dataSource
13025      * The underlying data store providing the paged data
13026      */
13027     /**
13028      * @cfg {String/HTMLElement/Element} container
13029      * container The id or element that will contain the toolbar
13030      */
13031     /**
13032      * @cfg {Boolean} displayInfo
13033      * True to display the displayMsg (defaults to false)
13034      */
13035     /**
13036      * @cfg {Number} pageSize
13037      * The number of records to display per page (defaults to 20)
13038      */
13039     pageSize: 20,
13040     /**
13041      * @cfg {String} displayMsg
13042      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
13043      */
13044     displayMsg : 'Displaying {0} - {1} of {2}',
13045     /**
13046      * @cfg {String} emptyMsg
13047      * The message to display when no records are found (defaults to "No data to display")
13048      */
13049     emptyMsg : 'No data to display',
13050     /**
13051      * Customizable piece of the default paging text (defaults to "Page")
13052      * @type String
13053      */
13054     beforePageText : "Page",
13055     /**
13056      * Customizable piece of the default paging text (defaults to "of %0")
13057      * @type String
13058      */
13059     afterPageText : "of {0}",
13060     /**
13061      * Customizable piece of the default paging text (defaults to "First Page")
13062      * @type String
13063      */
13064     firstText : "First Page",
13065     /**
13066      * Customizable piece of the default paging text (defaults to "Previous Page")
13067      * @type String
13068      */
13069     prevText : "Previous Page",
13070     /**
13071      * Customizable piece of the default paging text (defaults to "Next Page")
13072      * @type String
13073      */
13074     nextText : "Next Page",
13075     /**
13076      * Customizable piece of the default paging text (defaults to "Last Page")
13077      * @type String
13078      */
13079     lastText : "Last Page",
13080     /**
13081      * Customizable piece of the default paging text (defaults to "Refresh")
13082      * @type String
13083      */
13084     refreshText : "Refresh",
13085
13086     // private
13087     renderButtons : function(el){
13088         Roo.PagingToolbar.superclass.render.call(this, el);
13089         this.first = this.addButton({
13090             tooltip: this.firstText,
13091             cls: "x-btn-icon x-grid-page-first",
13092             disabled: true,
13093             handler: this.onClick.createDelegate(this, ["first"])
13094         });
13095         this.prev = this.addButton({
13096             tooltip: this.prevText,
13097             cls: "x-btn-icon x-grid-page-prev",
13098             disabled: true,
13099             handler: this.onClick.createDelegate(this, ["prev"])
13100         });
13101         //this.addSeparator();
13102         this.add(this.beforePageText);
13103         this.field = Roo.get(this.addDom({
13104            tag: "input",
13105            type: "text",
13106            size: "3",
13107            value: "1",
13108            cls: "x-grid-page-number"
13109         }).el);
13110         this.field.on("keydown", this.onPagingKeydown, this);
13111         this.field.on("focus", function(){this.dom.select();});
13112         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
13113         this.field.setHeight(18);
13114         //this.addSeparator();
13115         this.next = this.addButton({
13116             tooltip: this.nextText,
13117             cls: "x-btn-icon x-grid-page-next",
13118             disabled: true,
13119             handler: this.onClick.createDelegate(this, ["next"])
13120         });
13121         this.last = this.addButton({
13122             tooltip: this.lastText,
13123             cls: "x-btn-icon x-grid-page-last",
13124             disabled: true,
13125             handler: this.onClick.createDelegate(this, ["last"])
13126         });
13127         //this.addSeparator();
13128         this.loading = this.addButton({
13129             tooltip: this.refreshText,
13130             cls: "x-btn-icon x-grid-loading",
13131             handler: this.onClick.createDelegate(this, ["refresh"])
13132         });
13133
13134         if(this.displayInfo){
13135             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
13136         }
13137     },
13138
13139     // private
13140     updateInfo : function(){
13141         if(this.displayEl){
13142             var count = this.ds.getCount();
13143             var msg = count == 0 ?
13144                 this.emptyMsg :
13145                 String.format(
13146                     this.displayMsg,
13147                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
13148                 );
13149             this.displayEl.update(msg);
13150         }
13151     },
13152
13153     // private
13154     onLoad : function(ds, r, o){
13155        this.cursor = o.params ? o.params.start : 0;
13156        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
13157
13158        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
13159        this.field.dom.value = ap;
13160        this.first.setDisabled(ap == 1);
13161        this.prev.setDisabled(ap == 1);
13162        this.next.setDisabled(ap == ps);
13163        this.last.setDisabled(ap == ps);
13164        this.loading.enable();
13165        this.updateInfo();
13166     },
13167
13168     // private
13169     getPageData : function(){
13170         var total = this.ds.getTotalCount();
13171         return {
13172             total : total,
13173             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
13174             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
13175         };
13176     },
13177
13178     // private
13179     onLoadError : function(){
13180         this.loading.enable();
13181     },
13182
13183     // private
13184     onPagingKeydown : function(e){
13185         var k = e.getKey();
13186         var d = this.getPageData();
13187         if(k == e.RETURN){
13188             var v = this.field.dom.value, pageNum;
13189             if(!v || isNaN(pageNum = parseInt(v, 10))){
13190                 this.field.dom.value = d.activePage;
13191                 return;
13192             }
13193             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
13194             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13195             e.stopEvent();
13196         }
13197         else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
13198         {
13199           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
13200           this.field.dom.value = pageNum;
13201           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13202           e.stopEvent();
13203         }
13204         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13205         {
13206           var v = this.field.dom.value, pageNum; 
13207           var increment = (e.shiftKey) ? 10 : 1;
13208           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13209             increment *= -1;
13210           if(!v || isNaN(pageNum = parseInt(v, 10))) {
13211             this.field.dom.value = d.activePage;
13212             return;
13213           }
13214           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13215           {
13216             this.field.dom.value = parseInt(v, 10) + increment;
13217             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13218             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13219           }
13220           e.stopEvent();
13221         }
13222     },
13223
13224     // private
13225     beforeLoad : function(){
13226         if(this.loading){
13227             this.loading.disable();
13228         }
13229     },
13230
13231     // private
13232     onClick : function(which){
13233         var ds = this.ds;
13234         switch(which){
13235             case "first":
13236                 ds.load({params:{start: 0, limit: this.pageSize}});
13237             break;
13238             case "prev":
13239                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13240             break;
13241             case "next":
13242                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13243             break;
13244             case "last":
13245                 var total = ds.getTotalCount();
13246                 var extra = total % this.pageSize;
13247                 var lastStart = extra ? (total - extra) : total-this.pageSize;
13248                 ds.load({params:{start: lastStart, limit: this.pageSize}});
13249             break;
13250             case "refresh":
13251                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13252             break;
13253         }
13254     },
13255
13256     /**
13257      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13258      * @param {Roo.data.Store} store The data store to unbind
13259      */
13260     unbind : function(ds){
13261         ds.un("beforeload", this.beforeLoad, this);
13262         ds.un("load", this.onLoad, this);
13263         ds.un("loadexception", this.onLoadError, this);
13264         ds.un("remove", this.updateInfo, this);
13265         ds.un("add", this.updateInfo, this);
13266         this.ds = undefined;
13267     },
13268
13269     /**
13270      * Binds the paging toolbar to the specified {@link Roo.data.Store}
13271      * @param {Roo.data.Store} store The data store to bind
13272      */
13273     bind : function(ds){
13274         ds.on("beforeload", this.beforeLoad, this);
13275         ds.on("load", this.onLoad, this);
13276         ds.on("loadexception", this.onLoadError, this);
13277         ds.on("remove", this.updateInfo, this);
13278         ds.on("add", this.updateInfo, this);
13279         this.ds = ds;
13280     }
13281 });/*
13282  * Based on:
13283  * Ext JS Library 1.1.1
13284  * Copyright(c) 2006-2007, Ext JS, LLC.
13285  *
13286  * Originally Released Under LGPL - original licence link has changed is not relivant.
13287  *
13288  * Fork - LGPL
13289  * <script type="text/javascript">
13290  */
13291
13292 /**
13293  * @class Roo.Resizable
13294  * @extends Roo.util.Observable
13295  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13296  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13297  * the textarea in a div and set "resizeChild" to true (or to the id of the element), <b>or</b> set wrap:true in your config and
13298  * the element will be wrapped for you automatically.</p>
13299  * <p>Here is the list of valid resize handles:</p>
13300  * <pre>
13301 Value   Description
13302 ------  -------------------
13303  'n'     north
13304  's'     south
13305  'e'     east
13306  'w'     west
13307  'nw'    northwest
13308  'sw'    southwest
13309  'se'    southeast
13310  'ne'    northeast
13311  'hd'    horizontal drag
13312  'all'   all
13313 </pre>
13314  * <p>Here's an example showing the creation of a typical Resizable:</p>
13315  * <pre><code>
13316 var resizer = new Roo.Resizable("element-id", {
13317     handles: 'all',
13318     minWidth: 200,
13319     minHeight: 100,
13320     maxWidth: 500,
13321     maxHeight: 400,
13322     pinned: true
13323 });
13324 resizer.on("resize", myHandler);
13325 </code></pre>
13326  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13327  * resizer.east.setDisplayed(false);</p>
13328  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13329  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13330  * resize operation's new size (defaults to [0, 0])
13331  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13332  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13333  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13334  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13335  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13336  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13337  * @cfg {Number} width The width of the element in pixels (defaults to null)
13338  * @cfg {Number} height The height of the element in pixels (defaults to null)
13339  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13340  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13341  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13342  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13343  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13344  * in favor of the handles config option (defaults to false)
13345  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13346  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13347  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13348  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13349  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13350  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13351  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13352  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13353  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13354  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13355  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13356  * @constructor
13357  * Create a new resizable component
13358  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13359  * @param {Object} config configuration options
13360   */
13361 Roo.Resizable = function(el, config)
13362 {
13363     this.el = Roo.get(el);
13364
13365     if(config && config.wrap){
13366         config.resizeChild = this.el;
13367         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13368         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13369         this.el.setStyle("overflow", "hidden");
13370         this.el.setPositioning(config.resizeChild.getPositioning());
13371         config.resizeChild.clearPositioning();
13372         if(!config.width || !config.height){
13373             var csize = config.resizeChild.getSize();
13374             this.el.setSize(csize.width, csize.height);
13375         }
13376         if(config.pinned && !config.adjustments){
13377             config.adjustments = "auto";
13378         }
13379     }
13380
13381     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13382     this.proxy.unselectable();
13383     this.proxy.enableDisplayMode('block');
13384
13385     Roo.apply(this, config);
13386
13387     if(this.pinned){
13388         this.disableTrackOver = true;
13389         this.el.addClass("x-resizable-pinned");
13390     }
13391     // if the element isn't positioned, make it relative
13392     var position = this.el.getStyle("position");
13393     if(position != "absolute" && position != "fixed"){
13394         this.el.setStyle("position", "relative");
13395     }
13396     if(!this.handles){ // no handles passed, must be legacy style
13397         this.handles = 's,e,se';
13398         if(this.multiDirectional){
13399             this.handles += ',n,w';
13400         }
13401     }
13402     if(this.handles == "all"){
13403         this.handles = "n s e w ne nw se sw";
13404     }
13405     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13406     var ps = Roo.Resizable.positions;
13407     for(var i = 0, len = hs.length; i < len; i++){
13408         if(hs[i] && ps[hs[i]]){
13409             var pos = ps[hs[i]];
13410             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13411         }
13412     }
13413     // legacy
13414     this.corner = this.southeast;
13415     
13416     // updateBox = the box can move..
13417     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13418         this.updateBox = true;
13419     }
13420
13421     this.activeHandle = null;
13422
13423     if(this.resizeChild){
13424         if(typeof this.resizeChild == "boolean"){
13425             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13426         }else{
13427             this.resizeChild = Roo.get(this.resizeChild, true);
13428         }
13429     }
13430     
13431     if(this.adjustments == "auto"){
13432         var rc = this.resizeChild;
13433         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13434         if(rc && (hw || hn)){
13435             rc.position("relative");
13436             rc.setLeft(hw ? hw.el.getWidth() : 0);
13437             rc.setTop(hn ? hn.el.getHeight() : 0);
13438         }
13439         this.adjustments = [
13440             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13441             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13442         ];
13443     }
13444
13445     if(this.draggable){
13446         this.dd = this.dynamic ?
13447             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13448         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13449     }
13450
13451     // public events
13452     this.addEvents({
13453         /**
13454          * @event beforeresize
13455          * Fired before resize is allowed. Set enabled to false to cancel resize.
13456          * @param {Roo.Resizable} this
13457          * @param {Roo.EventObject} e The mousedown event
13458          */
13459         "beforeresize" : true,
13460         /**
13461          * @event resize
13462          * Fired after a resize.
13463          * @param {Roo.Resizable} this
13464          * @param {Number} width The new width
13465          * @param {Number} height The new height
13466          * @param {Roo.EventObject} e The mouseup event
13467          */
13468         "resize" : true
13469     });
13470
13471     if(this.width !== null && this.height !== null){
13472         this.resizeTo(this.width, this.height);
13473     }else{
13474         this.updateChildSize();
13475     }
13476     if(Roo.isIE){
13477         this.el.dom.style.zoom = 1;
13478     }
13479     Roo.Resizable.superclass.constructor.call(this);
13480 };
13481
13482 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13483         resizeChild : false,
13484         adjustments : [0, 0],
13485         minWidth : 5,
13486         minHeight : 5,
13487         maxWidth : 10000,
13488         maxHeight : 10000,
13489         enabled : true,
13490         animate : false,
13491         duration : .35,
13492         dynamic : false,
13493         handles : false,
13494         multiDirectional : false,
13495         disableTrackOver : false,
13496         easing : 'easeOutStrong',
13497         widthIncrement : 0,
13498         heightIncrement : 0,
13499         pinned : false,
13500         width : null,
13501         height : null,
13502         preserveRatio : false,
13503         transparent: false,
13504         minX: 0,
13505         minY: 0,
13506         draggable: false,
13507
13508         /**
13509          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13510          */
13511         constrainTo: undefined,
13512         /**
13513          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13514          */
13515         resizeRegion: undefined,
13516
13517
13518     /**
13519      * Perform a manual resize
13520      * @param {Number} width
13521      * @param {Number} height
13522      */
13523     resizeTo : function(width, height){
13524         this.el.setSize(width, height);
13525         this.updateChildSize();
13526         this.fireEvent("resize", this, width, height, null);
13527     },
13528
13529     // private
13530     startSizing : function(e, handle){
13531         this.fireEvent("beforeresize", this, e);
13532         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13533
13534             if(!this.overlay){
13535                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13536                 this.overlay.unselectable();
13537                 this.overlay.enableDisplayMode("block");
13538                 this.overlay.on("mousemove", this.onMouseMove, this);
13539                 this.overlay.on("mouseup", this.onMouseUp, this);
13540             }
13541             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13542
13543             this.resizing = true;
13544             this.startBox = this.el.getBox();
13545             this.startPoint = e.getXY();
13546             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13547                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13548
13549             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13550             this.overlay.show();
13551
13552             if(this.constrainTo) {
13553                 var ct = Roo.get(this.constrainTo);
13554                 this.resizeRegion = ct.getRegion().adjust(
13555                     ct.getFrameWidth('t'),
13556                     ct.getFrameWidth('l'),
13557                     -ct.getFrameWidth('b'),
13558                     -ct.getFrameWidth('r')
13559                 );
13560             }
13561
13562             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13563             this.proxy.show();
13564             this.proxy.setBox(this.startBox);
13565             if(!this.dynamic){
13566                 this.proxy.setStyle('visibility', 'visible');
13567             }
13568         }
13569     },
13570
13571     // private
13572     onMouseDown : function(handle, e){
13573         if(this.enabled){
13574             e.stopEvent();
13575             this.activeHandle = handle;
13576             this.startSizing(e, handle);
13577         }
13578     },
13579
13580     // private
13581     onMouseUp : function(e){
13582         var size = this.resizeElement();
13583         this.resizing = false;
13584         this.handleOut();
13585         this.overlay.hide();
13586         this.proxy.hide();
13587         this.fireEvent("resize", this, size.width, size.height, e);
13588     },
13589
13590     // private
13591     updateChildSize : function(){
13592         if(this.resizeChild){
13593             var el = this.el;
13594             var child = this.resizeChild;
13595             var adj = this.adjustments;
13596             if(el.dom.offsetWidth){
13597                 var b = el.getSize(true);
13598                 child.setSize(b.width+adj[0], b.height+adj[1]);
13599             }
13600             // Second call here for IE
13601             // The first call enables instant resizing and
13602             // the second call corrects scroll bars if they
13603             // exist
13604             if(Roo.isIE){
13605                 setTimeout(function(){
13606                     if(el.dom.offsetWidth){
13607                         var b = el.getSize(true);
13608                         child.setSize(b.width+adj[0], b.height+adj[1]);
13609                     }
13610                 }, 10);
13611             }
13612         }
13613     },
13614
13615     // private
13616     snap : function(value, inc, min){
13617         if(!inc || !value) return value;
13618         var newValue = value;
13619         var m = value % inc;
13620         if(m > 0){
13621             if(m > (inc/2)){
13622                 newValue = value + (inc-m);
13623             }else{
13624                 newValue = value - m;
13625             }
13626         }
13627         return Math.max(min, newValue);
13628     },
13629
13630     // private
13631     resizeElement : function(){
13632         var box = this.proxy.getBox();
13633         if(this.updateBox){
13634             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13635         }else{
13636             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13637         }
13638         this.updateChildSize();
13639         if(!this.dynamic){
13640             this.proxy.hide();
13641         }
13642         return box;
13643     },
13644
13645     // private
13646     constrain : function(v, diff, m, mx){
13647         if(v - diff < m){
13648             diff = v - m;
13649         }else if(v - diff > mx){
13650             diff = mx - v;
13651         }
13652         return diff;
13653     },
13654
13655     // private
13656     onMouseMove : function(e){
13657         if(this.enabled){
13658             try{// try catch so if something goes wrong the user doesn't get hung
13659
13660             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13661                 return;
13662             }
13663
13664             //var curXY = this.startPoint;
13665             var curSize = this.curSize || this.startBox;
13666             var x = this.startBox.x, y = this.startBox.y;
13667             var ox = x, oy = y;
13668             var w = curSize.width, h = curSize.height;
13669             var ow = w, oh = h;
13670             var mw = this.minWidth, mh = this.minHeight;
13671             var mxw = this.maxWidth, mxh = this.maxHeight;
13672             var wi = this.widthIncrement;
13673             var hi = this.heightIncrement;
13674
13675             var eventXY = e.getXY();
13676             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13677             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13678
13679             var pos = this.activeHandle.position;
13680
13681             switch(pos){
13682                 case "east":
13683                     w += diffX;
13684                     w = Math.min(Math.max(mw, w), mxw);
13685                     break;
13686              
13687                 case "south":
13688                     h += diffY;
13689                     h = Math.min(Math.max(mh, h), mxh);
13690                     break;
13691                 case "southeast":
13692                     w += diffX;
13693                     h += diffY;
13694                     w = Math.min(Math.max(mw, w), mxw);
13695                     h = Math.min(Math.max(mh, h), mxh);
13696                     break;
13697                 case "north":
13698                     diffY = this.constrain(h, diffY, mh, mxh);
13699                     y += diffY;
13700                     h -= diffY;
13701                     break;
13702                 case "hdrag":
13703                     
13704                     if (wi) {
13705                         var adiffX = Math.abs(diffX);
13706                         var sub = (adiffX % wi); // how much 
13707                         if (sub > (wi/2)) { // far enough to snap
13708                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13709                         } else {
13710                             // remove difference.. 
13711                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13712                         }
13713                     }
13714                     x += diffX;
13715                     x = Math.max(this.minX, x);
13716                     break;
13717                 case "west":
13718                     diffX = this.constrain(w, diffX, mw, mxw);
13719                     x += diffX;
13720                     w -= diffX;
13721                     break;
13722                 case "northeast":
13723                     w += diffX;
13724                     w = Math.min(Math.max(mw, w), mxw);
13725                     diffY = this.constrain(h, diffY, mh, mxh);
13726                     y += diffY;
13727                     h -= diffY;
13728                     break;
13729                 case "northwest":
13730                     diffX = this.constrain(w, diffX, mw, mxw);
13731                     diffY = this.constrain(h, diffY, mh, mxh);
13732                     y += diffY;
13733                     h -= diffY;
13734                     x += diffX;
13735                     w -= diffX;
13736                     break;
13737                case "southwest":
13738                     diffX = this.constrain(w, diffX, mw, mxw);
13739                     h += diffY;
13740                     h = Math.min(Math.max(mh, h), mxh);
13741                     x += diffX;
13742                     w -= diffX;
13743                     break;
13744             }
13745
13746             var sw = this.snap(w, wi, mw);
13747             var sh = this.snap(h, hi, mh);
13748             if(sw != w || sh != h){
13749                 switch(pos){
13750                     case "northeast":
13751                         y -= sh - h;
13752                     break;
13753                     case "north":
13754                         y -= sh - h;
13755                         break;
13756                     case "southwest":
13757                         x -= sw - w;
13758                     break;
13759                     case "west":
13760                         x -= sw - w;
13761                         break;
13762                     case "northwest":
13763                         x -= sw - w;
13764                         y -= sh - h;
13765                     break;
13766                 }
13767                 w = sw;
13768                 h = sh;
13769             }
13770
13771             if(this.preserveRatio){
13772                 switch(pos){
13773                     case "southeast":
13774                     case "east":
13775                         h = oh * (w/ow);
13776                         h = Math.min(Math.max(mh, h), mxh);
13777                         w = ow * (h/oh);
13778                        break;
13779                     case "south":
13780                         w = ow * (h/oh);
13781                         w = Math.min(Math.max(mw, w), mxw);
13782                         h = oh * (w/ow);
13783                         break;
13784                     case "northeast":
13785                         w = ow * (h/oh);
13786                         w = Math.min(Math.max(mw, w), mxw);
13787                         h = oh * (w/ow);
13788                     break;
13789                     case "north":
13790                         var tw = w;
13791                         w = ow * (h/oh);
13792                         w = Math.min(Math.max(mw, w), mxw);
13793                         h = oh * (w/ow);
13794                         x += (tw - w) / 2;
13795                         break;
13796                     case "southwest":
13797                         h = oh * (w/ow);
13798                         h = Math.min(Math.max(mh, h), mxh);
13799                         var tw = w;
13800                         w = ow * (h/oh);
13801                         x += tw - w;
13802                         break;
13803                     case "west":
13804                         var th = h;
13805                         h = oh * (w/ow);
13806                         h = Math.min(Math.max(mh, h), mxh);
13807                         y += (th - h) / 2;
13808                         var tw = w;
13809                         w = ow * (h/oh);
13810                         x += tw - w;
13811                        break;
13812                     case "northwest":
13813                         var tw = w;
13814                         var th = h;
13815                         h = oh * (w/ow);
13816                         h = Math.min(Math.max(mh, h), mxh);
13817                         w = ow * (h/oh);
13818                         y += th - h;
13819                         x += tw - w;
13820                        break;
13821
13822                 }
13823             }
13824             if (pos == 'hdrag') {
13825                 w = ow;
13826             }
13827             this.proxy.setBounds(x, y, w, h);
13828             if(this.dynamic){
13829                 this.resizeElement();
13830             }
13831             }catch(e){}
13832         }
13833     },
13834
13835     // private
13836     handleOver : function(){
13837         if(this.enabled){
13838             this.el.addClass("x-resizable-over");
13839         }
13840     },
13841
13842     // private
13843     handleOut : function(){
13844         if(!this.resizing){
13845             this.el.removeClass("x-resizable-over");
13846         }
13847     },
13848
13849     /**
13850      * Returns the element this component is bound to.
13851      * @return {Roo.Element}
13852      */
13853     getEl : function(){
13854         return this.el;
13855     },
13856
13857     /**
13858      * Returns the resizeChild element (or null).
13859      * @return {Roo.Element}
13860      */
13861     getResizeChild : function(){
13862         return this.resizeChild;
13863     },
13864
13865     /**
13866      * Destroys this resizable. If the element was wrapped and
13867      * removeEl is not true then the element remains.
13868      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13869      */
13870     destroy : function(removeEl){
13871         this.proxy.remove();
13872         if(this.overlay){
13873             this.overlay.removeAllListeners();
13874             this.overlay.remove();
13875         }
13876         var ps = Roo.Resizable.positions;
13877         for(var k in ps){
13878             if(typeof ps[k] != "function" && this[ps[k]]){
13879                 var h = this[ps[k]];
13880                 h.el.removeAllListeners();
13881                 h.el.remove();
13882             }
13883         }
13884         if(removeEl){
13885             this.el.update("");
13886             this.el.remove();
13887         }
13888     }
13889 });
13890
13891 // private
13892 // hash to map config positions to true positions
13893 Roo.Resizable.positions = {
13894     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13895     hd: "hdrag"
13896 };
13897
13898 // private
13899 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13900     if(!this.tpl){
13901         // only initialize the template if resizable is used
13902         var tpl = Roo.DomHelper.createTemplate(
13903             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13904         );
13905         tpl.compile();
13906         Roo.Resizable.Handle.prototype.tpl = tpl;
13907     }
13908     this.position = pos;
13909     this.rz = rz;
13910     // show north drag fro topdra
13911     var handlepos = pos == 'hdrag' ? 'north' : pos;
13912     
13913     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13914     if (pos == 'hdrag') {
13915         this.el.setStyle('cursor', 'pointer');
13916     }
13917     this.el.unselectable();
13918     if(transparent){
13919         this.el.setOpacity(0);
13920     }
13921     this.el.on("mousedown", this.onMouseDown, this);
13922     if(!disableTrackOver){
13923         this.el.on("mouseover", this.onMouseOver, this);
13924         this.el.on("mouseout", this.onMouseOut, this);
13925     }
13926 };
13927
13928 // private
13929 Roo.Resizable.Handle.prototype = {
13930     afterResize : function(rz){
13931         // do nothing
13932     },
13933     // private
13934     onMouseDown : function(e){
13935         this.rz.onMouseDown(this, e);
13936     },
13937     // private
13938     onMouseOver : function(e){
13939         this.rz.handleOver(this, e);
13940     },
13941     // private
13942     onMouseOut : function(e){
13943         this.rz.handleOut(this, e);
13944     }
13945 };/*
13946  * Based on:
13947  * Ext JS Library 1.1.1
13948  * Copyright(c) 2006-2007, Ext JS, LLC.
13949  *
13950  * Originally Released Under LGPL - original licence link has changed is not relivant.
13951  *
13952  * Fork - LGPL
13953  * <script type="text/javascript">
13954  */
13955
13956 /**
13957  * @class Roo.Editor
13958  * @extends Roo.Component
13959  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13960  * @constructor
13961  * Create a new Editor
13962  * @param {Roo.form.Field} field The Field object (or descendant)
13963  * @param {Object} config The config object
13964  */
13965 Roo.Editor = function(field, config){
13966     Roo.Editor.superclass.constructor.call(this, config);
13967     this.field = field;
13968     this.addEvents({
13969         /**
13970              * @event beforestartedit
13971              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13972              * false from the handler of this event.
13973              * @param {Editor} this
13974              * @param {Roo.Element} boundEl The underlying element bound to this editor
13975              * @param {Mixed} value The field value being set
13976              */
13977         "beforestartedit" : true,
13978         /**
13979              * @event startedit
13980              * Fires when this editor is displayed
13981              * @param {Roo.Element} boundEl The underlying element bound to this editor
13982              * @param {Mixed} value The starting field value
13983              */
13984         "startedit" : true,
13985         /**
13986              * @event beforecomplete
13987              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13988              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13989              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13990              * event will not fire since no edit actually occurred.
13991              * @param {Editor} this
13992              * @param {Mixed} value The current field value
13993              * @param {Mixed} startValue The original field value
13994              */
13995         "beforecomplete" : true,
13996         /**
13997              * @event complete
13998              * Fires after editing is complete and any changed value has been written to the underlying field.
13999              * @param {Editor} this
14000              * @param {Mixed} value The current field value
14001              * @param {Mixed} startValue The original field value
14002              */
14003         "complete" : true,
14004         /**
14005          * @event specialkey
14006          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
14007          * {@link Roo.EventObject#getKey} to determine which key was pressed.
14008          * @param {Roo.form.Field} this
14009          * @param {Roo.EventObject} e The event object
14010          */
14011         "specialkey" : true
14012     });
14013 };
14014
14015 Roo.extend(Roo.Editor, Roo.Component, {
14016     /**
14017      * @cfg {Boolean/String} autosize
14018      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
14019      * or "height" to adopt the height only (defaults to false)
14020      */
14021     /**
14022      * @cfg {Boolean} revertInvalid
14023      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
14024      * validation fails (defaults to true)
14025      */
14026     /**
14027      * @cfg {Boolean} ignoreNoChange
14028      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
14029      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
14030      * will never be ignored.
14031      */
14032     /**
14033      * @cfg {Boolean} hideEl
14034      * False to keep the bound element visible while the editor is displayed (defaults to true)
14035      */
14036     /**
14037      * @cfg {Mixed} value
14038      * The data value of the underlying field (defaults to "")
14039      */
14040     value : "",
14041     /**
14042      * @cfg {String} alignment
14043      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
14044      */
14045     alignment: "c-c?",
14046     /**
14047      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
14048      * for bottom-right shadow (defaults to "frame")
14049      */
14050     shadow : "frame",
14051     /**
14052      * @cfg {Boolean} constrain True to constrain the editor to the viewport
14053      */
14054     constrain : false,
14055     /**
14056      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
14057      */
14058     completeOnEnter : false,
14059     /**
14060      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
14061      */
14062     cancelOnEsc : false,
14063     /**
14064      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
14065      */
14066     updateEl : false,
14067
14068     // private
14069     onRender : function(ct, position){
14070         this.el = new Roo.Layer({
14071             shadow: this.shadow,
14072             cls: "x-editor",
14073             parentEl : ct,
14074             shim : this.shim,
14075             shadowOffset:4,
14076             id: this.id,
14077             constrain: this.constrain
14078         });
14079         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
14080         if(this.field.msgTarget != 'title'){
14081             this.field.msgTarget = 'qtip';
14082         }
14083         this.field.render(this.el);
14084         if(Roo.isGecko){
14085             this.field.el.dom.setAttribute('autocomplete', 'off');
14086         }
14087         this.field.on("specialkey", this.onSpecialKey, this);
14088         if(this.swallowKeys){
14089             this.field.el.swallowEvent(['keydown','keypress']);
14090         }
14091         this.field.show();
14092         this.field.on("blur", this.onBlur, this);
14093         if(this.field.grow){
14094             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
14095         }
14096     },
14097
14098     onSpecialKey : function(field, e)
14099     {
14100         //Roo.log('editor onSpecialKey');
14101         if(this.completeOnEnter && e.getKey() == e.ENTER){
14102             e.stopEvent();
14103             this.completeEdit();
14104             return;
14105         }
14106         // do not fire special key otherwise it might hide close the editor...
14107         if(e.getKey() == e.ENTER){    
14108             return;
14109         }
14110         if(this.cancelOnEsc && e.getKey() == e.ESC){
14111             this.cancelEdit();
14112             return;
14113         } 
14114         this.fireEvent('specialkey', field, e);
14115     
14116     },
14117
14118     /**
14119      * Starts the editing process and shows the editor.
14120      * @param {String/HTMLElement/Element} el The element to edit
14121      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
14122       * to the innerHTML of el.
14123      */
14124     startEdit : function(el, value){
14125         if(this.editing){
14126             this.completeEdit();
14127         }
14128         this.boundEl = Roo.get(el);
14129         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
14130         if(!this.rendered){
14131             this.render(this.parentEl || document.body);
14132         }
14133         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
14134             return;
14135         }
14136         this.startValue = v;
14137         this.field.setValue(v);
14138         if(this.autoSize){
14139             var sz = this.boundEl.getSize();
14140             switch(this.autoSize){
14141                 case "width":
14142                 this.setSize(sz.width,  "");
14143                 break;
14144                 case "height":
14145                 this.setSize("",  sz.height);
14146                 break;
14147                 default:
14148                 this.setSize(sz.width,  sz.height);
14149             }
14150         }
14151         this.el.alignTo(this.boundEl, this.alignment);
14152         this.editing = true;
14153         if(Roo.QuickTips){
14154             Roo.QuickTips.disable();
14155         }
14156         this.show();
14157     },
14158
14159     /**
14160      * Sets the height and width of this editor.
14161      * @param {Number} width The new width
14162      * @param {Number} height The new height
14163      */
14164     setSize : function(w, h){
14165         this.field.setSize(w, h);
14166         if(this.el){
14167             this.el.sync();
14168         }
14169     },
14170
14171     /**
14172      * Realigns the editor to the bound field based on the current alignment config value.
14173      */
14174     realign : function(){
14175         this.el.alignTo(this.boundEl, this.alignment);
14176     },
14177
14178     /**
14179      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
14180      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
14181      */
14182     completeEdit : function(remainVisible){
14183         if(!this.editing){
14184             return;
14185         }
14186         var v = this.getValue();
14187         if(this.revertInvalid !== false && !this.field.isValid()){
14188             v = this.startValue;
14189             this.cancelEdit(true);
14190         }
14191         if(String(v) === String(this.startValue) && this.ignoreNoChange){
14192             this.editing = false;
14193             this.hide();
14194             return;
14195         }
14196         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
14197             this.editing = false;
14198             if(this.updateEl && this.boundEl){
14199                 this.boundEl.update(v);
14200             }
14201             if(remainVisible !== true){
14202                 this.hide();
14203             }
14204             this.fireEvent("complete", this, v, this.startValue);
14205         }
14206     },
14207
14208     // private
14209     onShow : function(){
14210         this.el.show();
14211         if(this.hideEl !== false){
14212             this.boundEl.hide();
14213         }
14214         this.field.show();
14215         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14216             this.fixIEFocus = true;
14217             this.deferredFocus.defer(50, this);
14218         }else{
14219             this.field.focus();
14220         }
14221         this.fireEvent("startedit", this.boundEl, this.startValue);
14222     },
14223
14224     deferredFocus : function(){
14225         if(this.editing){
14226             this.field.focus();
14227         }
14228     },
14229
14230     /**
14231      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
14232      * reverted to the original starting value.
14233      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14234      * cancel (defaults to false)
14235      */
14236     cancelEdit : function(remainVisible){
14237         if(this.editing){
14238             this.setValue(this.startValue);
14239             if(remainVisible !== true){
14240                 this.hide();
14241             }
14242         }
14243     },
14244
14245     // private
14246     onBlur : function(){
14247         if(this.allowBlur !== true && this.editing){
14248             this.completeEdit();
14249         }
14250     },
14251
14252     // private
14253     onHide : function(){
14254         if(this.editing){
14255             this.completeEdit();
14256             return;
14257         }
14258         this.field.blur();
14259         if(this.field.collapse){
14260             this.field.collapse();
14261         }
14262         this.el.hide();
14263         if(this.hideEl !== false){
14264             this.boundEl.show();
14265         }
14266         if(Roo.QuickTips){
14267             Roo.QuickTips.enable();
14268         }
14269     },
14270
14271     /**
14272      * Sets the data value of the editor
14273      * @param {Mixed} value Any valid value supported by the underlying field
14274      */
14275     setValue : function(v){
14276         this.field.setValue(v);
14277     },
14278
14279     /**
14280      * Gets the data value of the editor
14281      * @return {Mixed} The data value
14282      */
14283     getValue : function(){
14284         return this.field.getValue();
14285     }
14286 });/*
14287  * Based on:
14288  * Ext JS Library 1.1.1
14289  * Copyright(c) 2006-2007, Ext JS, LLC.
14290  *
14291  * Originally Released Under LGPL - original licence link has changed is not relivant.
14292  *
14293  * Fork - LGPL
14294  * <script type="text/javascript">
14295  */
14296  
14297 /**
14298  * @class Roo.BasicDialog
14299  * @extends Roo.util.Observable
14300  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
14301  * <pre><code>
14302 var dlg = new Roo.BasicDialog("my-dlg", {
14303     height: 200,
14304     width: 300,
14305     minHeight: 100,
14306     minWidth: 150,
14307     modal: true,
14308     proxyDrag: true,
14309     shadow: true
14310 });
14311 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14312 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14313 dlg.addButton('Cancel', dlg.hide, dlg);
14314 dlg.show();
14315 </code></pre>
14316   <b>A Dialog should always be a direct child of the body element.</b>
14317  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14318  * @cfg {String} title Default text to display in the title bar (defaults to null)
14319  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14320  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14321  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14322  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14323  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14324  * (defaults to null with no animation)
14325  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14326  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14327  * property for valid values (defaults to 'all')
14328  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14329  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14330  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14331  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14332  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14333  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14334  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14335  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14336  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14337  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14338  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14339  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14340  * draggable = true (defaults to false)
14341  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14342  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14343  * shadow (defaults to false)
14344  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14345  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14346  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14347  * @cfg {Array} buttons Array of buttons
14348  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14349  * @constructor
14350  * Create a new BasicDialog.
14351  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14352  * @param {Object} config Configuration options
14353  */
14354 Roo.BasicDialog = function(el, config){
14355     this.el = Roo.get(el);
14356     var dh = Roo.DomHelper;
14357     if(!this.el && config && config.autoCreate){
14358         if(typeof config.autoCreate == "object"){
14359             if(!config.autoCreate.id){
14360                 config.autoCreate.id = el;
14361             }
14362             this.el = dh.append(document.body,
14363                         config.autoCreate, true);
14364         }else{
14365             this.el = dh.append(document.body,
14366                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14367         }
14368     }
14369     el = this.el;
14370     el.setDisplayed(true);
14371     el.hide = this.hideAction;
14372     this.id = el.id;
14373     el.addClass("x-dlg");
14374
14375     Roo.apply(this, config);
14376
14377     this.proxy = el.createProxy("x-dlg-proxy");
14378     this.proxy.hide = this.hideAction;
14379     this.proxy.setOpacity(.5);
14380     this.proxy.hide();
14381
14382     if(config.width){
14383         el.setWidth(config.width);
14384     }
14385     if(config.height){
14386         el.setHeight(config.height);
14387     }
14388     this.size = el.getSize();
14389     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14390         this.xy = [config.x,config.y];
14391     }else{
14392         this.xy = el.getCenterXY(true);
14393     }
14394     /** The header element @type Roo.Element */
14395     this.header = el.child("> .x-dlg-hd");
14396     /** The body element @type Roo.Element */
14397     this.body = el.child("> .x-dlg-bd");
14398     /** The footer element @type Roo.Element */
14399     this.footer = el.child("> .x-dlg-ft");
14400
14401     if(!this.header){
14402         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14403     }
14404     if(!this.body){
14405         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14406     }
14407
14408     this.header.unselectable();
14409     if(this.title){
14410         this.header.update(this.title);
14411     }
14412     // this element allows the dialog to be focused for keyboard event
14413     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14414     this.focusEl.swallowEvent("click", true);
14415
14416     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14417
14418     // wrap the body and footer for special rendering
14419     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14420     if(this.footer){
14421         this.bwrap.dom.appendChild(this.footer.dom);
14422     }
14423
14424     this.bg = this.el.createChild({
14425         tag: "div", cls:"x-dlg-bg",
14426         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14427     });
14428     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14429
14430
14431     if(this.autoScroll !== false && !this.autoTabs){
14432         this.body.setStyle("overflow", "auto");
14433     }
14434
14435     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14436
14437     if(this.closable !== false){
14438         this.el.addClass("x-dlg-closable");
14439         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14440         this.close.on("click", this.closeClick, this);
14441         this.close.addClassOnOver("x-dlg-close-over");
14442     }
14443     if(this.collapsible !== false){
14444         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14445         this.collapseBtn.on("click", this.collapseClick, this);
14446         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14447         this.header.on("dblclick", this.collapseClick, this);
14448     }
14449     if(this.resizable !== false){
14450         this.el.addClass("x-dlg-resizable");
14451         this.resizer = new Roo.Resizable(el, {
14452             minWidth: this.minWidth || 80,
14453             minHeight:this.minHeight || 80,
14454             handles: this.resizeHandles || "all",
14455             pinned: true
14456         });
14457         this.resizer.on("beforeresize", this.beforeResize, this);
14458         this.resizer.on("resize", this.onResize, this);
14459     }
14460     if(this.draggable !== false){
14461         el.addClass("x-dlg-draggable");
14462         if (!this.proxyDrag) {
14463             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14464         }
14465         else {
14466             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14467         }
14468         dd.setHandleElId(this.header.id);
14469         dd.endDrag = this.endMove.createDelegate(this);
14470         dd.startDrag = this.startMove.createDelegate(this);
14471         dd.onDrag = this.onDrag.createDelegate(this);
14472         dd.scroll = false;
14473         this.dd = dd;
14474     }
14475     if(this.modal){
14476         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14477         this.mask.enableDisplayMode("block");
14478         this.mask.hide();
14479         this.el.addClass("x-dlg-modal");
14480     }
14481     if(this.shadow){
14482         this.shadow = new Roo.Shadow({
14483             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14484             offset : this.shadowOffset
14485         });
14486     }else{
14487         this.shadowOffset = 0;
14488     }
14489     if(Roo.useShims && this.shim !== false){
14490         this.shim = this.el.createShim();
14491         this.shim.hide = this.hideAction;
14492         this.shim.hide();
14493     }else{
14494         this.shim = false;
14495     }
14496     if(this.autoTabs){
14497         this.initTabs();
14498     }
14499     if (this.buttons) { 
14500         var bts= this.buttons;
14501         this.buttons = [];
14502         Roo.each(bts, function(b) {
14503             this.addButton(b);
14504         }, this);
14505     }
14506     
14507     
14508     this.addEvents({
14509         /**
14510          * @event keydown
14511          * Fires when a key is pressed
14512          * @param {Roo.BasicDialog} this
14513          * @param {Roo.EventObject} e
14514          */
14515         "keydown" : true,
14516         /**
14517          * @event move
14518          * Fires when this dialog is moved by the user.
14519          * @param {Roo.BasicDialog} this
14520          * @param {Number} x The new page X
14521          * @param {Number} y The new page Y
14522          */
14523         "move" : true,
14524         /**
14525          * @event resize
14526          * Fires when this dialog is resized by the user.
14527          * @param {Roo.BasicDialog} this
14528          * @param {Number} width The new width
14529          * @param {Number} height The new height
14530          */
14531         "resize" : true,
14532         /**
14533          * @event beforehide
14534          * Fires before this dialog is hidden.
14535          * @param {Roo.BasicDialog} this
14536          */
14537         "beforehide" : true,
14538         /**
14539          * @event hide
14540          * Fires when this dialog is hidden.
14541          * @param {Roo.BasicDialog} this
14542          */
14543         "hide" : true,
14544         /**
14545          * @event beforeshow
14546          * Fires before this dialog is shown.
14547          * @param {Roo.BasicDialog} this
14548          */
14549         "beforeshow" : true,
14550         /**
14551          * @event show
14552          * Fires when this dialog is shown.
14553          * @param {Roo.BasicDialog} this
14554          */
14555         "show" : true
14556     });
14557     el.on("keydown", this.onKeyDown, this);
14558     el.on("mousedown", this.toFront, this);
14559     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14560     this.el.hide();
14561     Roo.DialogManager.register(this);
14562     Roo.BasicDialog.superclass.constructor.call(this);
14563 };
14564
14565 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14566     shadowOffset: Roo.isIE ? 6 : 5,
14567     minHeight: 80,
14568     minWidth: 200,
14569     minButtonWidth: 75,
14570     defaultButton: null,
14571     buttonAlign: "right",
14572     tabTag: 'div',
14573     firstShow: true,
14574
14575     /**
14576      * Sets the dialog title text
14577      * @param {String} text The title text to display
14578      * @return {Roo.BasicDialog} this
14579      */
14580     setTitle : function(text){
14581         this.header.update(text);
14582         return this;
14583     },
14584
14585     // private
14586     closeClick : function(){
14587         this.hide();
14588     },
14589
14590     // private
14591     collapseClick : function(){
14592         this[this.collapsed ? "expand" : "collapse"]();
14593     },
14594
14595     /**
14596      * Collapses the dialog to its minimized state (only the title bar is visible).
14597      * Equivalent to the user clicking the collapse dialog button.
14598      */
14599     collapse : function(){
14600         if(!this.collapsed){
14601             this.collapsed = true;
14602             this.el.addClass("x-dlg-collapsed");
14603             this.restoreHeight = this.el.getHeight();
14604             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14605         }
14606     },
14607
14608     /**
14609      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14610      * clicking the expand dialog button.
14611      */
14612     expand : function(){
14613         if(this.collapsed){
14614             this.collapsed = false;
14615             this.el.removeClass("x-dlg-collapsed");
14616             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14617         }
14618     },
14619
14620     /**
14621      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14622      * @return {Roo.TabPanel} The tabs component
14623      */
14624     initTabs : function(){
14625         var tabs = this.getTabs();
14626         while(tabs.getTab(0)){
14627             tabs.removeTab(0);
14628         }
14629         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14630             var dom = el.dom;
14631             tabs.addTab(Roo.id(dom), dom.title);
14632             dom.title = "";
14633         });
14634         tabs.activate(0);
14635         return tabs;
14636     },
14637
14638     // private
14639     beforeResize : function(){
14640         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14641     },
14642
14643     // private
14644     onResize : function(){
14645         this.refreshSize();
14646         this.syncBodyHeight();
14647         this.adjustAssets();
14648         this.focus();
14649         this.fireEvent("resize", this, this.size.width, this.size.height);
14650     },
14651
14652     // private
14653     onKeyDown : function(e){
14654         if(this.isVisible()){
14655             this.fireEvent("keydown", this, e);
14656         }
14657     },
14658
14659     /**
14660      * Resizes the dialog.
14661      * @param {Number} width
14662      * @param {Number} height
14663      * @return {Roo.BasicDialog} this
14664      */
14665     resizeTo : function(width, height){
14666         this.el.setSize(width, height);
14667         this.size = {width: width, height: height};
14668         this.syncBodyHeight();
14669         if(this.fixedcenter){
14670             this.center();
14671         }
14672         if(this.isVisible()){
14673             this.constrainXY();
14674             this.adjustAssets();
14675         }
14676         this.fireEvent("resize", this, width, height);
14677         return this;
14678     },
14679
14680
14681     /**
14682      * Resizes the dialog to fit the specified content size.
14683      * @param {Number} width
14684      * @param {Number} height
14685      * @return {Roo.BasicDialog} this
14686      */
14687     setContentSize : function(w, h){
14688         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14689         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14690         //if(!this.el.isBorderBox()){
14691             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14692             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14693         //}
14694         if(this.tabs){
14695             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14696             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14697         }
14698         this.resizeTo(w, h);
14699         return this;
14700     },
14701
14702     /**
14703      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14704      * executed in response to a particular key being pressed while the dialog is active.
14705      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14706      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14707      * @param {Function} fn The function to call
14708      * @param {Object} scope (optional) The scope of the function
14709      * @return {Roo.BasicDialog} this
14710      */
14711     addKeyListener : function(key, fn, scope){
14712         var keyCode, shift, ctrl, alt;
14713         if(typeof key == "object" && !(key instanceof Array)){
14714             keyCode = key["key"];
14715             shift = key["shift"];
14716             ctrl = key["ctrl"];
14717             alt = key["alt"];
14718         }else{
14719             keyCode = key;
14720         }
14721         var handler = function(dlg, e){
14722             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14723                 var k = e.getKey();
14724                 if(keyCode instanceof Array){
14725                     for(var i = 0, len = keyCode.length; i < len; i++){
14726                         if(keyCode[i] == k){
14727                           fn.call(scope || window, dlg, k, e);
14728                           return;
14729                         }
14730                     }
14731                 }else{
14732                     if(k == keyCode){
14733                         fn.call(scope || window, dlg, k, e);
14734                     }
14735                 }
14736             }
14737         };
14738         this.on("keydown", handler);
14739         return this;
14740     },
14741
14742     /**
14743      * Returns the TabPanel component (creates it if it doesn't exist).
14744      * Note: If you wish to simply check for the existence of tabs without creating them,
14745      * check for a null 'tabs' property.
14746      * @return {Roo.TabPanel} The tabs component
14747      */
14748     getTabs : function(){
14749         if(!this.tabs){
14750             this.el.addClass("x-dlg-auto-tabs");
14751             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14752             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14753         }
14754         return this.tabs;
14755     },
14756
14757     /**
14758      * Adds a button to the footer section of the dialog.
14759      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14760      * object or a valid Roo.DomHelper element config
14761      * @param {Function} handler The function called when the button is clicked
14762      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14763      * @return {Roo.Button} The new button
14764      */
14765     addButton : function(config, handler, scope){
14766         var dh = Roo.DomHelper;
14767         if(!this.footer){
14768             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14769         }
14770         if(!this.btnContainer){
14771             var tb = this.footer.createChild({
14772
14773                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14774                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14775             }, null, true);
14776             this.btnContainer = tb.firstChild.firstChild.firstChild;
14777         }
14778         var bconfig = {
14779             handler: handler,
14780             scope: scope,
14781             minWidth: this.minButtonWidth,
14782             hideParent:true
14783         };
14784         if(typeof config == "string"){
14785             bconfig.text = config;
14786         }else{
14787             if(config.tag){
14788                 bconfig.dhconfig = config;
14789             }else{
14790                 Roo.apply(bconfig, config);
14791             }
14792         }
14793         var fc = false;
14794         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14795             bconfig.position = Math.max(0, bconfig.position);
14796             fc = this.btnContainer.childNodes[bconfig.position];
14797         }
14798          
14799         var btn = new Roo.Button(
14800             fc ? 
14801                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14802                 : this.btnContainer.appendChild(document.createElement("td")),
14803             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14804             bconfig
14805         );
14806         this.syncBodyHeight();
14807         if(!this.buttons){
14808             /**
14809              * Array of all the buttons that have been added to this dialog via addButton
14810              * @type Array
14811              */
14812             this.buttons = [];
14813         }
14814         this.buttons.push(btn);
14815         return btn;
14816     },
14817
14818     /**
14819      * Sets the default button to be focused when the dialog is displayed.
14820      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14821      * @return {Roo.BasicDialog} this
14822      */
14823     setDefaultButton : function(btn){
14824         this.defaultButton = btn;
14825         return this;
14826     },
14827
14828     // private
14829     getHeaderFooterHeight : function(safe){
14830         var height = 0;
14831         if(this.header){
14832            height += this.header.getHeight();
14833         }
14834         if(this.footer){
14835            var fm = this.footer.getMargins();
14836             height += (this.footer.getHeight()+fm.top+fm.bottom);
14837         }
14838         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14839         height += this.centerBg.getPadding("tb");
14840         return height;
14841     },
14842
14843     // private
14844     syncBodyHeight : function(){
14845         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
14846         var height = this.size.height - this.getHeaderFooterHeight(false);
14847         bd.setHeight(height-bd.getMargins("tb"));
14848         var hh = this.header.getHeight();
14849         var h = this.size.height-hh;
14850         cb.setHeight(h);
14851         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14852         bw.setHeight(h-cb.getPadding("tb"));
14853         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14854         bd.setWidth(bw.getWidth(true));
14855         if(this.tabs){
14856             this.tabs.syncHeight();
14857             if(Roo.isIE){
14858                 this.tabs.el.repaint();
14859             }
14860         }
14861     },
14862
14863     /**
14864      * Restores the previous state of the dialog if Roo.state is configured.
14865      * @return {Roo.BasicDialog} this
14866      */
14867     restoreState : function(){
14868         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14869         if(box && box.width){
14870             this.xy = [box.x, box.y];
14871             this.resizeTo(box.width, box.height);
14872         }
14873         return this;
14874     },
14875
14876     // private
14877     beforeShow : function(){
14878         this.expand();
14879         if(this.fixedcenter){
14880             this.xy = this.el.getCenterXY(true);
14881         }
14882         if(this.modal){
14883             Roo.get(document.body).addClass("x-body-masked");
14884             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14885             this.mask.show();
14886         }
14887         this.constrainXY();
14888     },
14889
14890     // private
14891     animShow : function(){
14892         var b = Roo.get(this.animateTarget).getBox();
14893         this.proxy.setSize(b.width, b.height);
14894         this.proxy.setLocation(b.x, b.y);
14895         this.proxy.show();
14896         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14897                     true, .35, this.showEl.createDelegate(this));
14898     },
14899
14900     /**
14901      * Shows the dialog.
14902      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14903      * @return {Roo.BasicDialog} this
14904      */
14905     show : function(animateTarget){
14906         if (this.fireEvent("beforeshow", this) === false){
14907             return;
14908         }
14909         if(this.syncHeightBeforeShow){
14910             this.syncBodyHeight();
14911         }else if(this.firstShow){
14912             this.firstShow = false;
14913             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14914         }
14915         this.animateTarget = animateTarget || this.animateTarget;
14916         if(!this.el.isVisible()){
14917             this.beforeShow();
14918             if(this.animateTarget && Roo.get(this.animateTarget)){
14919                 this.animShow();
14920             }else{
14921                 this.showEl();
14922             }
14923         }
14924         return this;
14925     },
14926
14927     // private
14928     showEl : function(){
14929         this.proxy.hide();
14930         this.el.setXY(this.xy);
14931         this.el.show();
14932         this.adjustAssets(true);
14933         this.toFront();
14934         this.focus();
14935         // IE peekaboo bug - fix found by Dave Fenwick
14936         if(Roo.isIE){
14937             this.el.repaint();
14938         }
14939         this.fireEvent("show", this);
14940     },
14941
14942     /**
14943      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14944      * dialog itself will receive focus.
14945      */
14946     focus : function(){
14947         if(this.defaultButton){
14948             this.defaultButton.focus();
14949         }else{
14950             this.focusEl.focus();
14951         }
14952     },
14953
14954     // private
14955     constrainXY : function(){
14956         if(this.constraintoviewport !== false){
14957             if(!this.viewSize){
14958                 if(this.container){
14959                     var s = this.container.getSize();
14960                     this.viewSize = [s.width, s.height];
14961                 }else{
14962                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14963                 }
14964             }
14965             var s = Roo.get(this.container||document).getScroll();
14966
14967             var x = this.xy[0], y = this.xy[1];
14968             var w = this.size.width, h = this.size.height;
14969             var vw = this.viewSize[0], vh = this.viewSize[1];
14970             // only move it if it needs it
14971             var moved = false;
14972             // first validate right/bottom
14973             if(x + w > vw+s.left){
14974                 x = vw - w;
14975                 moved = true;
14976             }
14977             if(y + h > vh+s.top){
14978                 y = vh - h;
14979                 moved = true;
14980             }
14981             // then make sure top/left isn't negative
14982             if(x < s.left){
14983                 x = s.left;
14984                 moved = true;
14985             }
14986             if(y < s.top){
14987                 y = s.top;
14988                 moved = true;
14989             }
14990             if(moved){
14991                 // cache xy
14992                 this.xy = [x, y];
14993                 if(this.isVisible()){
14994                     this.el.setLocation(x, y);
14995                     this.adjustAssets();
14996                 }
14997             }
14998         }
14999     },
15000
15001     // private
15002     onDrag : function(){
15003         if(!this.proxyDrag){
15004             this.xy = this.el.getXY();
15005             this.adjustAssets();
15006         }
15007     },
15008
15009     // private
15010     adjustAssets : function(doShow){
15011         var x = this.xy[0], y = this.xy[1];
15012         var w = this.size.width, h = this.size.height;
15013         if(doShow === true){
15014             if(this.shadow){
15015                 this.shadow.show(this.el);
15016             }
15017             if(this.shim){
15018                 this.shim.show();
15019             }
15020         }
15021         if(this.shadow && this.shadow.isVisible()){
15022             this.shadow.show(this.el);
15023         }
15024         if(this.shim && this.shim.isVisible()){
15025             this.shim.setBounds(x, y, w, h);
15026         }
15027     },
15028
15029     // private
15030     adjustViewport : function(w, h){
15031         if(!w || !h){
15032             w = Roo.lib.Dom.getViewWidth();
15033             h = Roo.lib.Dom.getViewHeight();
15034         }
15035         // cache the size
15036         this.viewSize = [w, h];
15037         if(this.modal && this.mask.isVisible()){
15038             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
15039             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15040         }
15041         if(this.isVisible()){
15042             this.constrainXY();
15043         }
15044     },
15045
15046     /**
15047      * Destroys this dialog and all its supporting elements (including any tabs, shim,
15048      * shadow, proxy, mask, etc.)  Also removes all event listeners.
15049      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
15050      */
15051     destroy : function(removeEl){
15052         if(this.isVisible()){
15053             this.animateTarget = null;
15054             this.hide();
15055         }
15056         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
15057         if(this.tabs){
15058             this.tabs.destroy(removeEl);
15059         }
15060         Roo.destroy(
15061              this.shim,
15062              this.proxy,
15063              this.resizer,
15064              this.close,
15065              this.mask
15066         );
15067         if(this.dd){
15068             this.dd.unreg();
15069         }
15070         if(this.buttons){
15071            for(var i = 0, len = this.buttons.length; i < len; i++){
15072                this.buttons[i].destroy();
15073            }
15074         }
15075         this.el.removeAllListeners();
15076         if(removeEl === true){
15077             this.el.update("");
15078             this.el.remove();
15079         }
15080         Roo.DialogManager.unregister(this);
15081     },
15082
15083     // private
15084     startMove : function(){
15085         if(this.proxyDrag){
15086             this.proxy.show();
15087         }
15088         if(this.constraintoviewport !== false){
15089             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
15090         }
15091     },
15092
15093     // private
15094     endMove : function(){
15095         if(!this.proxyDrag){
15096             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
15097         }else{
15098             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
15099             this.proxy.hide();
15100         }
15101         this.refreshSize();
15102         this.adjustAssets();
15103         this.focus();
15104         this.fireEvent("move", this, this.xy[0], this.xy[1]);
15105     },
15106
15107     /**
15108      * Brings this dialog to the front of any other visible dialogs
15109      * @return {Roo.BasicDialog} this
15110      */
15111     toFront : function(){
15112         Roo.DialogManager.bringToFront(this);
15113         return this;
15114     },
15115
15116     /**
15117      * Sends this dialog to the back (under) of any other visible dialogs
15118      * @return {Roo.BasicDialog} this
15119      */
15120     toBack : function(){
15121         Roo.DialogManager.sendToBack(this);
15122         return this;
15123     },
15124
15125     /**
15126      * Centers this dialog in the viewport
15127      * @return {Roo.BasicDialog} this
15128      */
15129     center : function(){
15130         var xy = this.el.getCenterXY(true);
15131         this.moveTo(xy[0], xy[1]);
15132         return this;
15133     },
15134
15135     /**
15136      * Moves the dialog's top-left corner to the specified point
15137      * @param {Number} x
15138      * @param {Number} y
15139      * @return {Roo.BasicDialog} this
15140      */
15141     moveTo : function(x, y){
15142         this.xy = [x,y];
15143         if(this.isVisible()){
15144             this.el.setXY(this.xy);
15145             this.adjustAssets();
15146         }
15147         return this;
15148     },
15149
15150     /**
15151      * Aligns the dialog to the specified element
15152      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15153      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
15154      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15155      * @return {Roo.BasicDialog} this
15156      */
15157     alignTo : function(element, position, offsets){
15158         this.xy = this.el.getAlignToXY(element, position, offsets);
15159         if(this.isVisible()){
15160             this.el.setXY(this.xy);
15161             this.adjustAssets();
15162         }
15163         return this;
15164     },
15165
15166     /**
15167      * Anchors an element to another element and realigns it when the window is resized.
15168      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15169      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
15170      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15171      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
15172      * is a number, it is used as the buffer delay (defaults to 50ms).
15173      * @return {Roo.BasicDialog} this
15174      */
15175     anchorTo : function(el, alignment, offsets, monitorScroll){
15176         var action = function(){
15177             this.alignTo(el, alignment, offsets);
15178         };
15179         Roo.EventManager.onWindowResize(action, this);
15180         var tm = typeof monitorScroll;
15181         if(tm != 'undefined'){
15182             Roo.EventManager.on(window, 'scroll', action, this,
15183                 {buffer: tm == 'number' ? monitorScroll : 50});
15184         }
15185         action.call(this);
15186         return this;
15187     },
15188
15189     /**
15190      * Returns true if the dialog is visible
15191      * @return {Boolean}
15192      */
15193     isVisible : function(){
15194         return this.el.isVisible();
15195     },
15196
15197     // private
15198     animHide : function(callback){
15199         var b = Roo.get(this.animateTarget).getBox();
15200         this.proxy.show();
15201         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
15202         this.el.hide();
15203         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
15204                     this.hideEl.createDelegate(this, [callback]));
15205     },
15206
15207     /**
15208      * Hides the dialog.
15209      * @param {Function} callback (optional) Function to call when the dialog is hidden
15210      * @return {Roo.BasicDialog} this
15211      */
15212     hide : function(callback){
15213         if (this.fireEvent("beforehide", this) === false){
15214             return;
15215         }
15216         if(this.shadow){
15217             this.shadow.hide();
15218         }
15219         if(this.shim) {
15220           this.shim.hide();
15221         }
15222         // sometimes animateTarget seems to get set.. causing problems...
15223         // this just double checks..
15224         if(this.animateTarget && Roo.get(this.animateTarget)) {
15225            this.animHide(callback);
15226         }else{
15227             this.el.hide();
15228             this.hideEl(callback);
15229         }
15230         return this;
15231     },
15232
15233     // private
15234     hideEl : function(callback){
15235         this.proxy.hide();
15236         if(this.modal){
15237             this.mask.hide();
15238             Roo.get(document.body).removeClass("x-body-masked");
15239         }
15240         this.fireEvent("hide", this);
15241         if(typeof callback == "function"){
15242             callback();
15243         }
15244     },
15245
15246     // private
15247     hideAction : function(){
15248         this.setLeft("-10000px");
15249         this.setTop("-10000px");
15250         this.setStyle("visibility", "hidden");
15251     },
15252
15253     // private
15254     refreshSize : function(){
15255         this.size = this.el.getSize();
15256         this.xy = this.el.getXY();
15257         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15258     },
15259
15260     // private
15261     // z-index is managed by the DialogManager and may be overwritten at any time
15262     setZIndex : function(index){
15263         if(this.modal){
15264             this.mask.setStyle("z-index", index);
15265         }
15266         if(this.shim){
15267             this.shim.setStyle("z-index", ++index);
15268         }
15269         if(this.shadow){
15270             this.shadow.setZIndex(++index);
15271         }
15272         this.el.setStyle("z-index", ++index);
15273         if(this.proxy){
15274             this.proxy.setStyle("z-index", ++index);
15275         }
15276         if(this.resizer){
15277             this.resizer.proxy.setStyle("z-index", ++index);
15278         }
15279
15280         this.lastZIndex = index;
15281     },
15282
15283     /**
15284      * Returns the element for this dialog
15285      * @return {Roo.Element} The underlying dialog Element
15286      */
15287     getEl : function(){
15288         return this.el;
15289     }
15290 });
15291
15292 /**
15293  * @class Roo.DialogManager
15294  * Provides global access to BasicDialogs that have been created and
15295  * support for z-indexing (layering) multiple open dialogs.
15296  */
15297 Roo.DialogManager = function(){
15298     var list = {};
15299     var accessList = [];
15300     var front = null;
15301
15302     // private
15303     var sortDialogs = function(d1, d2){
15304         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15305     };
15306
15307     // private
15308     var orderDialogs = function(){
15309         accessList.sort(sortDialogs);
15310         var seed = Roo.DialogManager.zseed;
15311         for(var i = 0, len = accessList.length; i < len; i++){
15312             var dlg = accessList[i];
15313             if(dlg){
15314                 dlg.setZIndex(seed + (i*10));
15315             }
15316         }
15317     };
15318
15319     return {
15320         /**
15321          * The starting z-index for BasicDialogs (defaults to 9000)
15322          * @type Number The z-index value
15323          */
15324         zseed : 9000,
15325
15326         // private
15327         register : function(dlg){
15328             list[dlg.id] = dlg;
15329             accessList.push(dlg);
15330         },
15331
15332         // private
15333         unregister : function(dlg){
15334             delete list[dlg.id];
15335             var i=0;
15336             var len=0;
15337             if(!accessList.indexOf){
15338                 for(  i = 0, len = accessList.length; i < len; i++){
15339                     if(accessList[i] == dlg){
15340                         accessList.splice(i, 1);
15341                         return;
15342                     }
15343                 }
15344             }else{
15345                  i = accessList.indexOf(dlg);
15346                 if(i != -1){
15347                     accessList.splice(i, 1);
15348                 }
15349             }
15350         },
15351
15352         /**
15353          * Gets a registered dialog by id
15354          * @param {String/Object} id The id of the dialog or a dialog
15355          * @return {Roo.BasicDialog} this
15356          */
15357         get : function(id){
15358             return typeof id == "object" ? id : list[id];
15359         },
15360
15361         /**
15362          * Brings the specified dialog to the front
15363          * @param {String/Object} dlg The id of the dialog or a dialog
15364          * @return {Roo.BasicDialog} this
15365          */
15366         bringToFront : function(dlg){
15367             dlg = this.get(dlg);
15368             if(dlg != front){
15369                 front = dlg;
15370                 dlg._lastAccess = new Date().getTime();
15371                 orderDialogs();
15372             }
15373             return dlg;
15374         },
15375
15376         /**
15377          * Sends the specified dialog to the back
15378          * @param {String/Object} dlg The id of the dialog or a dialog
15379          * @return {Roo.BasicDialog} this
15380          */
15381         sendToBack : function(dlg){
15382             dlg = this.get(dlg);
15383             dlg._lastAccess = -(new Date().getTime());
15384             orderDialogs();
15385             return dlg;
15386         },
15387
15388         /**
15389          * Hides all dialogs
15390          */
15391         hideAll : function(){
15392             for(var id in list){
15393                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15394                     list[id].hide();
15395                 }
15396             }
15397         }
15398     };
15399 }();
15400
15401 /**
15402  * @class Roo.LayoutDialog
15403  * @extends Roo.BasicDialog
15404  * Dialog which provides adjustments for working with a layout in a Dialog.
15405  * Add your necessary layout config options to the dialog's config.<br>
15406  * Example usage (including a nested layout):
15407  * <pre><code>
15408 if(!dialog){
15409     dialog = new Roo.LayoutDialog("download-dlg", {
15410         modal: true,
15411         width:600,
15412         height:450,
15413         shadow:true,
15414         minWidth:500,
15415         minHeight:350,
15416         autoTabs:true,
15417         proxyDrag:true,
15418         // layout config merges with the dialog config
15419         center:{
15420             tabPosition: "top",
15421             alwaysShowTabs: true
15422         }
15423     });
15424     dialog.addKeyListener(27, dialog.hide, dialog);
15425     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15426     dialog.addButton("Build It!", this.getDownload, this);
15427
15428     // we can even add nested layouts
15429     var innerLayout = new Roo.BorderLayout("dl-inner", {
15430         east: {
15431             initialSize: 200,
15432             autoScroll:true,
15433             split:true
15434         },
15435         center: {
15436             autoScroll:true
15437         }
15438     });
15439     innerLayout.beginUpdate();
15440     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15441     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15442     innerLayout.endUpdate(true);
15443
15444     var layout = dialog.getLayout();
15445     layout.beginUpdate();
15446     layout.add("center", new Roo.ContentPanel("standard-panel",
15447                         {title: "Download the Source", fitToFrame:true}));
15448     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15449                {title: "Build your own roo.js"}));
15450     layout.getRegion("center").showPanel(sp);
15451     layout.endUpdate();
15452 }
15453 </code></pre>
15454     * @constructor
15455     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15456     * @param {Object} config configuration options
15457   */
15458 Roo.LayoutDialog = function(el, cfg){
15459     
15460     var config=  cfg;
15461     if (typeof(cfg) == 'undefined') {
15462         config = Roo.apply({}, el);
15463         // not sure why we use documentElement here.. - it should always be body.
15464         // IE7 borks horribly if we use documentElement.
15465         // webkit also does not like documentElement - it creates a body element...
15466         el = Roo.get( document.body || document.documentElement ).createChild();
15467         //config.autoCreate = true;
15468     }
15469     
15470     
15471     config.autoTabs = false;
15472     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15473     this.body.setStyle({overflow:"hidden", position:"relative"});
15474     this.layout = new Roo.BorderLayout(this.body.dom, config);
15475     this.layout.monitorWindowResize = false;
15476     this.el.addClass("x-dlg-auto-layout");
15477     // fix case when center region overwrites center function
15478     this.center = Roo.BasicDialog.prototype.center;
15479     this.on("show", this.layout.layout, this.layout, true);
15480     if (config.items) {
15481         var xitems = config.items;
15482         delete config.items;
15483         Roo.each(xitems, this.addxtype, this);
15484     }
15485     
15486     
15487 };
15488 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15489     /**
15490      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15491      * @deprecated
15492      */
15493     endUpdate : function(){
15494         this.layout.endUpdate();
15495     },
15496
15497     /**
15498      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15499      *  @deprecated
15500      */
15501     beginUpdate : function(){
15502         this.layout.beginUpdate();
15503     },
15504
15505     /**
15506      * Get the BorderLayout for this dialog
15507      * @return {Roo.BorderLayout}
15508      */
15509     getLayout : function(){
15510         return this.layout;
15511     },
15512
15513     showEl : function(){
15514         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15515         if(Roo.isIE7){
15516             this.layout.layout();
15517         }
15518     },
15519
15520     // private
15521     // Use the syncHeightBeforeShow config option to control this automatically
15522     syncBodyHeight : function(){
15523         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15524         if(this.layout){this.layout.layout();}
15525     },
15526     
15527       /**
15528      * Add an xtype element (actually adds to the layout.)
15529      * @return {Object} xdata xtype object data.
15530      */
15531     
15532     addxtype : function(c) {
15533         return this.layout.addxtype(c);
15534     }
15535 });/*
15536  * Based on:
15537  * Ext JS Library 1.1.1
15538  * Copyright(c) 2006-2007, Ext JS, LLC.
15539  *
15540  * Originally Released Under LGPL - original licence link has changed is not relivant.
15541  *
15542  * Fork - LGPL
15543  * <script type="text/javascript">
15544  */
15545  
15546 /**
15547  * @class Roo.MessageBox
15548  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15549  * Example usage:
15550  *<pre><code>
15551 // Basic alert:
15552 Roo.Msg.alert('Status', 'Changes saved successfully.');
15553
15554 // Prompt for user data:
15555 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15556     if (btn == 'ok'){
15557         // process text value...
15558     }
15559 });
15560
15561 // Show a dialog using config options:
15562 Roo.Msg.show({
15563    title:'Save Changes?',
15564    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15565    buttons: Roo.Msg.YESNOCANCEL,
15566    fn: processResult,
15567    animEl: 'elId'
15568 });
15569 </code></pre>
15570  * @singleton
15571  */
15572 Roo.MessageBox = function(){
15573     var dlg, opt, mask, waitTimer;
15574     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15575     var buttons, activeTextEl, bwidth;
15576
15577     // private
15578     var handleButton = function(button){
15579         dlg.hide();
15580         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15581     };
15582
15583     // private
15584     var handleHide = function(){
15585         if(opt && opt.cls){
15586             dlg.el.removeClass(opt.cls);
15587         }
15588         if(waitTimer){
15589             Roo.TaskMgr.stop(waitTimer);
15590             waitTimer = null;
15591         }
15592     };
15593
15594     // private
15595     var updateButtons = function(b){
15596         var width = 0;
15597         if(!b){
15598             buttons["ok"].hide();
15599             buttons["cancel"].hide();
15600             buttons["yes"].hide();
15601             buttons["no"].hide();
15602             dlg.footer.dom.style.display = 'none';
15603             return width;
15604         }
15605         dlg.footer.dom.style.display = '';
15606         for(var k in buttons){
15607             if(typeof buttons[k] != "function"){
15608                 if(b[k]){
15609                     buttons[k].show();
15610                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15611                     width += buttons[k].el.getWidth()+15;
15612                 }else{
15613                     buttons[k].hide();
15614                 }
15615             }
15616         }
15617         return width;
15618     };
15619
15620     // private
15621     var handleEsc = function(d, k, e){
15622         if(opt && opt.closable !== false){
15623             dlg.hide();
15624         }
15625         if(e){
15626             e.stopEvent();
15627         }
15628     };
15629
15630     return {
15631         /**
15632          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15633          * @return {Roo.BasicDialog} The BasicDialog element
15634          */
15635         getDialog : function(){
15636            if(!dlg){
15637                 dlg = new Roo.BasicDialog("x-msg-box", {
15638                     autoCreate : true,
15639                     shadow: true,
15640                     draggable: true,
15641                     resizable:false,
15642                     constraintoviewport:false,
15643                     fixedcenter:true,
15644                     collapsible : false,
15645                     shim:true,
15646                     modal: true,
15647                     width:400, height:100,
15648                     buttonAlign:"center",
15649                     closeClick : function(){
15650                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15651                             handleButton("no");
15652                         }else{
15653                             handleButton("cancel");
15654                         }
15655                     }
15656                 });
15657                 dlg.on("hide", handleHide);
15658                 mask = dlg.mask;
15659                 dlg.addKeyListener(27, handleEsc);
15660                 buttons = {};
15661                 var bt = this.buttonText;
15662                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15663                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15664                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15665                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15666                 bodyEl = dlg.body.createChild({
15667
15668                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" /><textarea class="roo-mb-textarea"></textarea><div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
15669                 });
15670                 msgEl = bodyEl.dom.firstChild;
15671                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15672                 textboxEl.enableDisplayMode();
15673                 textboxEl.addKeyListener([10,13], function(){
15674                     if(dlg.isVisible() && opt && opt.buttons){
15675                         if(opt.buttons.ok){
15676                             handleButton("ok");
15677                         }else if(opt.buttons.yes){
15678                             handleButton("yes");
15679                         }
15680                     }
15681                 });
15682                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15683                 textareaEl.enableDisplayMode();
15684                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15685                 progressEl.enableDisplayMode();
15686                 var pf = progressEl.dom.firstChild;
15687                 if (pf) {
15688                     pp = Roo.get(pf.firstChild);
15689                     pp.setHeight(pf.offsetHeight);
15690                 }
15691                 
15692             }
15693             return dlg;
15694         },
15695
15696         /**
15697          * Updates the message box body text
15698          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15699          * the XHTML-compliant non-breaking space character '&amp;#160;')
15700          * @return {Roo.MessageBox} This message box
15701          */
15702         updateText : function(text){
15703             if(!dlg.isVisible() && !opt.width){
15704                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15705             }
15706             msgEl.innerHTML = text || '&#160;';
15707       
15708             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
15709             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
15710             var w = Math.max(
15711                     Math.min(opt.width || cw , this.maxWidth), 
15712                     Math.max(opt.minWidth || this.minWidth, bwidth)
15713             );
15714             if(opt.prompt){
15715                 activeTextEl.setWidth(w);
15716             }
15717             if(dlg.isVisible()){
15718                 dlg.fixedcenter = false;
15719             }
15720             // to big, make it scroll. = But as usual stupid IE does not support
15721             // !important..
15722             
15723             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
15724                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
15725                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
15726             } else {
15727                 bodyEl.dom.style.height = '';
15728                 bodyEl.dom.style.overflowY = '';
15729             }
15730             if (cw > w) {
15731                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
15732             } else {
15733                 bodyEl.dom.style.overflowX = '';
15734             }
15735             
15736             dlg.setContentSize(w, bodyEl.getHeight());
15737             if(dlg.isVisible()){
15738                 dlg.fixedcenter = true;
15739             }
15740             return this;
15741         },
15742
15743         /**
15744          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15745          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15746          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15747          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15748          * @return {Roo.MessageBox} This message box
15749          */
15750         updateProgress : function(value, text){
15751             if(text){
15752                 this.updateText(text);
15753             }
15754             if (pp) { // weird bug on my firefox - for some reason this is not defined
15755                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15756             }
15757             return this;
15758         },        
15759
15760         /**
15761          * Returns true if the message box is currently displayed
15762          * @return {Boolean} True if the message box is visible, else false
15763          */
15764         isVisible : function(){
15765             return dlg && dlg.isVisible();  
15766         },
15767
15768         /**
15769          * Hides the message box if it is displayed
15770          */
15771         hide : function(){
15772             if(this.isVisible()){
15773                 dlg.hide();
15774             }  
15775         },
15776
15777         /**
15778          * Displays a new message box, or reinitializes an existing message box, based on the config options
15779          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15780          * The following config object properties are supported:
15781          * <pre>
15782 Property    Type             Description
15783 ----------  ---------------  ------------------------------------------------------------------------------------
15784 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15785                                    closes (defaults to undefined)
15786 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15787                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15788 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15789                                    progress and wait dialogs will ignore this property and always hide the
15790                                    close button as they can only be closed programmatically.
15791 cls               String           A custom CSS class to apply to the message box element
15792 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15793                                    displayed (defaults to 75)
15794 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15795                                    function will be btn (the name of the button that was clicked, if applicable,
15796                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15797                                    Progress and wait dialogs will ignore this option since they do not respond to
15798                                    user actions and can only be closed programmatically, so any required function
15799                                    should be called by the same code after it closes the dialog.
15800 icon              String           A CSS class that provides a background image to be used as an icon for
15801                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15802 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15803 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15804 modal             Boolean          False to allow user interaction with the page while the message box is
15805                                    displayed (defaults to true)
15806 msg               String           A string that will replace the existing message box body text (defaults
15807                                    to the XHTML-compliant non-breaking space character '&#160;')
15808 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15809 progress          Boolean          True to display a progress bar (defaults to false)
15810 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15811 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15812 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15813 title             String           The title text
15814 value             String           The string value to set into the active textbox element if displayed
15815 wait              Boolean          True to display a progress bar (defaults to false)
15816 width             Number           The width of the dialog in pixels
15817 </pre>
15818          *
15819          * Example usage:
15820          * <pre><code>
15821 Roo.Msg.show({
15822    title: 'Address',
15823    msg: 'Please enter your address:',
15824    width: 300,
15825    buttons: Roo.MessageBox.OKCANCEL,
15826    multiline: true,
15827    fn: saveAddress,
15828    animEl: 'addAddressBtn'
15829 });
15830 </code></pre>
15831          * @param {Object} config Configuration options
15832          * @return {Roo.MessageBox} This message box
15833          */
15834         show : function(options)
15835         {
15836             
15837             // this causes nightmares if you show one dialog after another
15838             // especially on callbacks..
15839              
15840             if(this.isVisible()){
15841                 
15842                 this.hide();
15843                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
15844                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
15845                 Roo.log("New Dialog Message:" +  options.msg )
15846                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
15847                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
15848                 
15849             }
15850             var d = this.getDialog();
15851             opt = options;
15852             d.setTitle(opt.title || "&#160;");
15853             d.close.setDisplayed(opt.closable !== false);
15854             activeTextEl = textboxEl;
15855             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15856             if(opt.prompt){
15857                 if(opt.multiline){
15858                     textboxEl.hide();
15859                     textareaEl.show();
15860                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15861                         opt.multiline : this.defaultTextHeight);
15862                     activeTextEl = textareaEl;
15863                 }else{
15864                     textboxEl.show();
15865                     textareaEl.hide();
15866                 }
15867             }else{
15868                 textboxEl.hide();
15869                 textareaEl.hide();
15870             }
15871             progressEl.setDisplayed(opt.progress === true);
15872             this.updateProgress(0);
15873             activeTextEl.dom.value = opt.value || "";
15874             if(opt.prompt){
15875                 dlg.setDefaultButton(activeTextEl);
15876             }else{
15877                 var bs = opt.buttons;
15878                 var db = null;
15879                 if(bs && bs.ok){
15880                     db = buttons["ok"];
15881                 }else if(bs && bs.yes){
15882                     db = buttons["yes"];
15883                 }
15884                 dlg.setDefaultButton(db);
15885             }
15886             bwidth = updateButtons(opt.buttons);
15887             this.updateText(opt.msg);
15888             if(opt.cls){
15889                 d.el.addClass(opt.cls);
15890             }
15891             d.proxyDrag = opt.proxyDrag === true;
15892             d.modal = opt.modal !== false;
15893             d.mask = opt.modal !== false ? mask : false;
15894             if(!d.isVisible()){
15895                 // force it to the end of the z-index stack so it gets a cursor in FF
15896                 document.body.appendChild(dlg.el.dom);
15897                 d.animateTarget = null;
15898                 d.show(options.animEl);
15899             }
15900             return this;
15901         },
15902
15903         /**
15904          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15905          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15906          * and closing the message box when the process is complete.
15907          * @param {String} title The title bar text
15908          * @param {String} msg The message box body text
15909          * @return {Roo.MessageBox} This message box
15910          */
15911         progress : function(title, msg){
15912             this.show({
15913                 title : title,
15914                 msg : msg,
15915                 buttons: false,
15916                 progress:true,
15917                 closable:false,
15918                 minWidth: this.minProgressWidth,
15919                 modal : true
15920             });
15921             return this;
15922         },
15923
15924         /**
15925          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15926          * If a callback function is passed it will be called after the user clicks the button, and the
15927          * id of the button that was clicked will be passed as the only parameter to the callback
15928          * (could also be the top-right close button).
15929          * @param {String} title The title bar text
15930          * @param {String} msg The message box body text
15931          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15932          * @param {Object} scope (optional) The scope of the callback function
15933          * @return {Roo.MessageBox} This message box
15934          */
15935         alert : function(title, msg, fn, scope){
15936             this.show({
15937                 title : title,
15938                 msg : msg,
15939                 buttons: this.OK,
15940                 fn: fn,
15941                 scope : scope,
15942                 modal : true
15943             });
15944             return this;
15945         },
15946
15947         /**
15948          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15949          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15950          * You are responsible for closing the message box when the process is complete.
15951          * @param {String} msg The message box body text
15952          * @param {String} title (optional) The title bar text
15953          * @return {Roo.MessageBox} This message box
15954          */
15955         wait : function(msg, title){
15956             this.show({
15957                 title : title,
15958                 msg : msg,
15959                 buttons: false,
15960                 closable:false,
15961                 progress:true,
15962                 modal:true,
15963                 width:300,
15964                 wait:true
15965             });
15966             waitTimer = Roo.TaskMgr.start({
15967                 run: function(i){
15968                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15969                 },
15970                 interval: 1000
15971             });
15972             return this;
15973         },
15974
15975         /**
15976          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15977          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15978          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15979          * @param {String} title The title bar text
15980          * @param {String} msg The message box body text
15981          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15982          * @param {Object} scope (optional) The scope of the callback function
15983          * @return {Roo.MessageBox} This message box
15984          */
15985         confirm : function(title, msg, fn, scope){
15986             this.show({
15987                 title : title,
15988                 msg : msg,
15989                 buttons: this.YESNO,
15990                 fn: fn,
15991                 scope : scope,
15992                 modal : true
15993             });
15994             return this;
15995         },
15996
15997         /**
15998          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15999          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
16000          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
16001          * (could also be the top-right close button) and the text that was entered will be passed as the two
16002          * parameters to the callback.
16003          * @param {String} title The title bar text
16004          * @param {String} msg The message box body text
16005          * @param {Function} fn (optional) The callback function invoked after the message box is closed
16006          * @param {Object} scope (optional) The scope of the callback function
16007          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
16008          * property, or the height in pixels to create the textbox (defaults to false / single-line)
16009          * @return {Roo.MessageBox} This message box
16010          */
16011         prompt : function(title, msg, fn, scope, multiline){
16012             this.show({
16013                 title : title,
16014                 msg : msg,
16015                 buttons: this.OKCANCEL,
16016                 fn: fn,
16017                 minWidth:250,
16018                 scope : scope,
16019                 prompt:true,
16020                 multiline: multiline,
16021                 modal : true
16022             });
16023             return this;
16024         },
16025
16026         /**
16027          * Button config that displays a single OK button
16028          * @type Object
16029          */
16030         OK : {ok:true},
16031         /**
16032          * Button config that displays Yes and No buttons
16033          * @type Object
16034          */
16035         YESNO : {yes:true, no:true},
16036         /**
16037          * Button config that displays OK and Cancel buttons
16038          * @type Object
16039          */
16040         OKCANCEL : {ok:true, cancel:true},
16041         /**
16042          * Button config that displays Yes, No and Cancel buttons
16043          * @type Object
16044          */
16045         YESNOCANCEL : {yes:true, no:true, cancel:true},
16046
16047         /**
16048          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
16049          * @type Number
16050          */
16051         defaultTextHeight : 75,
16052         /**
16053          * The maximum width in pixels of the message box (defaults to 600)
16054          * @type Number
16055          */
16056         maxWidth : 600,
16057         /**
16058          * The minimum width in pixels of the message box (defaults to 100)
16059          * @type Number
16060          */
16061         minWidth : 100,
16062         /**
16063          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
16064          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
16065          * @type Number
16066          */
16067         minProgressWidth : 250,
16068         /**
16069          * An object containing the default button text strings that can be overriden for localized language support.
16070          * Supported properties are: ok, cancel, yes and no.
16071          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
16072          * @type Object
16073          */
16074         buttonText : {
16075             ok : "OK",
16076             cancel : "Cancel",
16077             yes : "Yes",
16078             no : "No"
16079         }
16080     };
16081 }();
16082
16083 /**
16084  * Shorthand for {@link Roo.MessageBox}
16085  */
16086 Roo.Msg = Roo.MessageBox;/*
16087  * Based on:
16088  * Ext JS Library 1.1.1
16089  * Copyright(c) 2006-2007, Ext JS, LLC.
16090  *
16091  * Originally Released Under LGPL - original licence link has changed is not relivant.
16092  *
16093  * Fork - LGPL
16094  * <script type="text/javascript">
16095  */
16096 /**
16097  * @class Roo.QuickTips
16098  * Provides attractive and customizable tooltips for any element.
16099  * @singleton
16100  */
16101 Roo.QuickTips = function(){
16102     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
16103     var ce, bd, xy, dd;
16104     var visible = false, disabled = true, inited = false;
16105     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
16106     
16107     var onOver = function(e){
16108         if(disabled){
16109             return;
16110         }
16111         var t = e.getTarget();
16112         if(!t || t.nodeType !== 1 || t == document || t == document.body){
16113             return;
16114         }
16115         if(ce && t == ce.el){
16116             clearTimeout(hideProc);
16117             return;
16118         }
16119         if(t && tagEls[t.id]){
16120             tagEls[t.id].el = t;
16121             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
16122             return;
16123         }
16124         var ttp, et = Roo.fly(t);
16125         var ns = cfg.namespace;
16126         if(tm.interceptTitles && t.title){
16127             ttp = t.title;
16128             t.qtip = ttp;
16129             t.removeAttribute("title");
16130             e.preventDefault();
16131         }else{
16132             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
16133         }
16134         if(ttp){
16135             showProc = show.defer(tm.showDelay, tm, [{
16136                 el: t, 
16137                 text: ttp, 
16138                 width: et.getAttributeNS(ns, cfg.width),
16139                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
16140                 title: et.getAttributeNS(ns, cfg.title),
16141                     cls: et.getAttributeNS(ns, cfg.cls)
16142             }]);
16143         }
16144     };
16145     
16146     var onOut = function(e){
16147         clearTimeout(showProc);
16148         var t = e.getTarget();
16149         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
16150             hideProc = setTimeout(hide, tm.hideDelay);
16151         }
16152     };
16153     
16154     var onMove = function(e){
16155         if(disabled){
16156             return;
16157         }
16158         xy = e.getXY();
16159         xy[1] += 18;
16160         if(tm.trackMouse && ce){
16161             el.setXY(xy);
16162         }
16163     };
16164     
16165     var onDown = function(e){
16166         clearTimeout(showProc);
16167         clearTimeout(hideProc);
16168         if(!e.within(el)){
16169             if(tm.hideOnClick){
16170                 hide();
16171                 tm.disable();
16172                 tm.enable.defer(100, tm);
16173             }
16174         }
16175     };
16176     
16177     var getPad = function(){
16178         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
16179     };
16180
16181     var show = function(o){
16182         if(disabled){
16183             return;
16184         }
16185         clearTimeout(dismissProc);
16186         ce = o;
16187         if(removeCls){ // in case manually hidden
16188             el.removeClass(removeCls);
16189             removeCls = null;
16190         }
16191         if(ce.cls){
16192             el.addClass(ce.cls);
16193             removeCls = ce.cls;
16194         }
16195         if(ce.title){
16196             tipTitle.update(ce.title);
16197             tipTitle.show();
16198         }else{
16199             tipTitle.update('');
16200             tipTitle.hide();
16201         }
16202         el.dom.style.width  = tm.maxWidth+'px';
16203         //tipBody.dom.style.width = '';
16204         tipBodyText.update(o.text);
16205         var p = getPad(), w = ce.width;
16206         if(!w){
16207             var td = tipBodyText.dom;
16208             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
16209             if(aw > tm.maxWidth){
16210                 w = tm.maxWidth;
16211             }else if(aw < tm.minWidth){
16212                 w = tm.minWidth;
16213             }else{
16214                 w = aw;
16215             }
16216         }
16217         //tipBody.setWidth(w);
16218         el.setWidth(parseInt(w, 10) + p);
16219         if(ce.autoHide === false){
16220             close.setDisplayed(true);
16221             if(dd){
16222                 dd.unlock();
16223             }
16224         }else{
16225             close.setDisplayed(false);
16226             if(dd){
16227                 dd.lock();
16228             }
16229         }
16230         if(xy){
16231             el.avoidY = xy[1]-18;
16232             el.setXY(xy);
16233         }
16234         if(tm.animate){
16235             el.setOpacity(.1);
16236             el.setStyle("visibility", "visible");
16237             el.fadeIn({callback: afterShow});
16238         }else{
16239             afterShow();
16240         }
16241     };
16242     
16243     var afterShow = function(){
16244         if(ce){
16245             el.show();
16246             esc.enable();
16247             if(tm.autoDismiss && ce.autoHide !== false){
16248                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16249             }
16250         }
16251     };
16252     
16253     var hide = function(noanim){
16254         clearTimeout(dismissProc);
16255         clearTimeout(hideProc);
16256         ce = null;
16257         if(el.isVisible()){
16258             esc.disable();
16259             if(noanim !== true && tm.animate){
16260                 el.fadeOut({callback: afterHide});
16261             }else{
16262                 afterHide();
16263             } 
16264         }
16265     };
16266     
16267     var afterHide = function(){
16268         el.hide();
16269         if(removeCls){
16270             el.removeClass(removeCls);
16271             removeCls = null;
16272         }
16273     };
16274     
16275     return {
16276         /**
16277         * @cfg {Number} minWidth
16278         * The minimum width of the quick tip (defaults to 40)
16279         */
16280        minWidth : 40,
16281         /**
16282         * @cfg {Number} maxWidth
16283         * The maximum width of the quick tip (defaults to 300)
16284         */
16285        maxWidth : 300,
16286         /**
16287         * @cfg {Boolean} interceptTitles
16288         * True to automatically use the element's DOM title value if available (defaults to false)
16289         */
16290        interceptTitles : false,
16291         /**
16292         * @cfg {Boolean} trackMouse
16293         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16294         */
16295        trackMouse : false,
16296         /**
16297         * @cfg {Boolean} hideOnClick
16298         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16299         */
16300        hideOnClick : true,
16301         /**
16302         * @cfg {Number} showDelay
16303         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16304         */
16305        showDelay : 500,
16306         /**
16307         * @cfg {Number} hideDelay
16308         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16309         */
16310        hideDelay : 200,
16311         /**
16312         * @cfg {Boolean} autoHide
16313         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16314         * Used in conjunction with hideDelay.
16315         */
16316        autoHide : true,
16317         /**
16318         * @cfg {Boolean}
16319         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16320         * (defaults to true).  Used in conjunction with autoDismissDelay.
16321         */
16322        autoDismiss : true,
16323         /**
16324         * @cfg {Number}
16325         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16326         */
16327        autoDismissDelay : 5000,
16328        /**
16329         * @cfg {Boolean} animate
16330         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16331         */
16332        animate : false,
16333
16334        /**
16335         * @cfg {String} title
16336         * Title text to display (defaults to '').  This can be any valid HTML markup.
16337         */
16338         title: '',
16339        /**
16340         * @cfg {String} text
16341         * Body text to display (defaults to '').  This can be any valid HTML markup.
16342         */
16343         text : '',
16344        /**
16345         * @cfg {String} cls
16346         * A CSS class to apply to the base quick tip element (defaults to '').
16347         */
16348         cls : '',
16349        /**
16350         * @cfg {Number} width
16351         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16352         * minWidth or maxWidth.
16353         */
16354         width : null,
16355
16356     /**
16357      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16358      * or display QuickTips in a page.
16359      */
16360        init : function(){
16361           tm = Roo.QuickTips;
16362           cfg = tm.tagConfig;
16363           if(!inited){
16364               if(!Roo.isReady){ // allow calling of init() before onReady
16365                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16366                   return;
16367               }
16368               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16369               el.fxDefaults = {stopFx: true};
16370               // maximum custom styling
16371               //el.update('<div class="x-tip-top-left"><div class="x-tip-top-right"><div class="x-tip-top"></div></div></div><div class="x-tip-bd-left"><div class="x-tip-bd-right"><div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div></div></div><div class="x-tip-ft-left"><div class="x-tip-ft-right"><div class="x-tip-ft"></div></div></div>');
16372               el.update('<div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div>');              
16373               tipTitle = el.child('h3');
16374               tipTitle.enableDisplayMode("block");
16375               tipBody = el.child('div.x-tip-bd');
16376               tipBodyText = el.child('div.x-tip-bd-inner');
16377               //bdLeft = el.child('div.x-tip-bd-left');
16378               //bdRight = el.child('div.x-tip-bd-right');
16379               close = el.child('div.x-tip-close');
16380               close.enableDisplayMode("block");
16381               close.on("click", hide);
16382               var d = Roo.get(document);
16383               d.on("mousedown", onDown);
16384               d.on("mouseover", onOver);
16385               d.on("mouseout", onOut);
16386               d.on("mousemove", onMove);
16387               esc = d.addKeyListener(27, hide);
16388               esc.disable();
16389               if(Roo.dd.DD){
16390                   dd = el.initDD("default", null, {
16391                       onDrag : function(){
16392                           el.sync();  
16393                       }
16394                   });
16395                   dd.setHandleElId(tipTitle.id);
16396                   dd.lock();
16397               }
16398               inited = true;
16399           }
16400           this.enable(); 
16401        },
16402
16403     /**
16404      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16405      * are supported:
16406      * <pre>
16407 Property    Type                   Description
16408 ----------  ---------------------  ------------------------------------------------------------------------
16409 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16410      * </ul>
16411      * @param {Object} config The config object
16412      */
16413        register : function(config){
16414            var cs = config instanceof Array ? config : arguments;
16415            for(var i = 0, len = cs.length; i < len; i++) {
16416                var c = cs[i];
16417                var target = c.target;
16418                if(target){
16419                    if(target instanceof Array){
16420                        for(var j = 0, jlen = target.length; j < jlen; j++){
16421                            tagEls[target[j]] = c;
16422                        }
16423                    }else{
16424                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16425                    }
16426                }
16427            }
16428        },
16429
16430     /**
16431      * Removes this quick tip from its element and destroys it.
16432      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16433      */
16434        unregister : function(el){
16435            delete tagEls[Roo.id(el)];
16436        },
16437
16438     /**
16439      * Enable this quick tip.
16440      */
16441        enable : function(){
16442            if(inited && disabled){
16443                locks.pop();
16444                if(locks.length < 1){
16445                    disabled = false;
16446                }
16447            }
16448        },
16449
16450     /**
16451      * Disable this quick tip.
16452      */
16453        disable : function(){
16454           disabled = true;
16455           clearTimeout(showProc);
16456           clearTimeout(hideProc);
16457           clearTimeout(dismissProc);
16458           if(ce){
16459               hide(true);
16460           }
16461           locks.push(1);
16462        },
16463
16464     /**
16465      * Returns true if the quick tip is enabled, else false.
16466      */
16467        isEnabled : function(){
16468             return !disabled;
16469        },
16470
16471         // private
16472        tagConfig : {
16473            namespace : "ext",
16474            attribute : "qtip",
16475            width : "width",
16476            target : "target",
16477            title : "qtitle",
16478            hide : "hide",
16479            cls : "qclass"
16480        }
16481    };
16482 }();
16483
16484 // backwards compat
16485 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16486  * Based on:
16487  * Ext JS Library 1.1.1
16488  * Copyright(c) 2006-2007, Ext JS, LLC.
16489  *
16490  * Originally Released Under LGPL - original licence link has changed is not relivant.
16491  *
16492  * Fork - LGPL
16493  * <script type="text/javascript">
16494  */
16495  
16496
16497 /**
16498  * @class Roo.tree.TreePanel
16499  * @extends Roo.data.Tree
16500
16501  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16502  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16503  * @cfg {Boolean} enableDD true to enable drag and drop
16504  * @cfg {Boolean} enableDrag true to enable just drag
16505  * @cfg {Boolean} enableDrop true to enable just drop
16506  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16507  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16508  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16509  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16510  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16511  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16512  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16513  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16514  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16515  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16516  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16517  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16518  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
16519  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16520  * @cfg {Function} renderer DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes. to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
16521  * @cfg {Function} rendererTip DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes hovertip to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
16522  * 
16523  * @constructor
16524  * @param {String/HTMLElement/Element} el The container element
16525  * @param {Object} config
16526  */
16527 Roo.tree.TreePanel = function(el, config){
16528     var root = false;
16529     var loader = false;
16530     if (config.root) {
16531         root = config.root;
16532         delete config.root;
16533     }
16534     if (config.loader) {
16535         loader = config.loader;
16536         delete config.loader;
16537     }
16538     
16539     Roo.apply(this, config);
16540     Roo.tree.TreePanel.superclass.constructor.call(this);
16541     this.el = Roo.get(el);
16542     this.el.addClass('x-tree');
16543     //console.log(root);
16544     if (root) {
16545         this.setRootNode( Roo.factory(root, Roo.tree));
16546     }
16547     if (loader) {
16548         this.loader = Roo.factory(loader, Roo.tree);
16549     }
16550    /**
16551     * Read-only. The id of the container element becomes this TreePanel's id.
16552     */
16553     this.id = this.el.id;
16554     this.addEvents({
16555         /**
16556         * @event beforeload
16557         * Fires before a node is loaded, return false to cancel
16558         * @param {Node} node The node being loaded
16559         */
16560         "beforeload" : true,
16561         /**
16562         * @event load
16563         * Fires when a node is loaded
16564         * @param {Node} node The node that was loaded
16565         */
16566         "load" : true,
16567         /**
16568         * @event textchange
16569         * Fires when the text for a node is changed
16570         * @param {Node} node The node
16571         * @param {String} text The new text
16572         * @param {String} oldText The old text
16573         */
16574         "textchange" : true,
16575         /**
16576         * @event beforeexpand
16577         * Fires before a node is expanded, return false to cancel.
16578         * @param {Node} node The node
16579         * @param {Boolean} deep
16580         * @param {Boolean} anim
16581         */
16582         "beforeexpand" : true,
16583         /**
16584         * @event beforecollapse
16585         * Fires before a node is collapsed, return false to cancel.
16586         * @param {Node} node The node
16587         * @param {Boolean} deep
16588         * @param {Boolean} anim
16589         */
16590         "beforecollapse" : true,
16591         /**
16592         * @event expand
16593         * Fires when a node is expanded
16594         * @param {Node} node The node
16595         */
16596         "expand" : true,
16597         /**
16598         * @event disabledchange
16599         * Fires when the disabled status of a node changes
16600         * @param {Node} node The node
16601         * @param {Boolean} disabled
16602         */
16603         "disabledchange" : true,
16604         /**
16605         * @event collapse
16606         * Fires when a node is collapsed
16607         * @param {Node} node The node
16608         */
16609         "collapse" : true,
16610         /**
16611         * @event beforeclick
16612         * Fires before click processing on a node. Return false to cancel the default action.
16613         * @param {Node} node The node
16614         * @param {Roo.EventObject} e The event object
16615         */
16616         "beforeclick":true,
16617         /**
16618         * @event checkchange
16619         * Fires when a node with a checkbox's checked property changes
16620         * @param {Node} this This node
16621         * @param {Boolean} checked
16622         */
16623         "checkchange":true,
16624         /**
16625         * @event click
16626         * Fires when a node is clicked
16627         * @param {Node} node The node
16628         * @param {Roo.EventObject} e The event object
16629         */
16630         "click":true,
16631         /**
16632         * @event dblclick
16633         * Fires when a node is double clicked
16634         * @param {Node} node The node
16635         * @param {Roo.EventObject} e The event object
16636         */
16637         "dblclick":true,
16638         /**
16639         * @event contextmenu
16640         * Fires when a node is right clicked
16641         * @param {Node} node The node
16642         * @param {Roo.EventObject} e The event object
16643         */
16644         "contextmenu":true,
16645         /**
16646         * @event beforechildrenrendered
16647         * Fires right before the child nodes for a node are rendered
16648         * @param {Node} node The node
16649         */
16650         "beforechildrenrendered":true,
16651         /**
16652         * @event startdrag
16653         * Fires when a node starts being dragged
16654         * @param {Roo.tree.TreePanel} this
16655         * @param {Roo.tree.TreeNode} node
16656         * @param {event} e The raw browser event
16657         */ 
16658        "startdrag" : true,
16659        /**
16660         * @event enddrag
16661         * Fires when a drag operation is complete
16662         * @param {Roo.tree.TreePanel} this
16663         * @param {Roo.tree.TreeNode} node
16664         * @param {event} e The raw browser event
16665         */
16666        "enddrag" : true,
16667        /**
16668         * @event dragdrop
16669         * Fires when a dragged node is dropped on a valid DD target
16670         * @param {Roo.tree.TreePanel} this
16671         * @param {Roo.tree.TreeNode} node
16672         * @param {DD} dd The dd it was dropped on
16673         * @param {event} e The raw browser event
16674         */
16675        "dragdrop" : true,
16676        /**
16677         * @event beforenodedrop
16678         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16679         * passed to handlers has the following properties:<br />
16680         * <ul style="padding:5px;padding-left:16px;">
16681         * <li>tree - The TreePanel</li>
16682         * <li>target - The node being targeted for the drop</li>
16683         * <li>data - The drag data from the drag source</li>
16684         * <li>point - The point of the drop - append, above or below</li>
16685         * <li>source - The drag source</li>
16686         * <li>rawEvent - Raw mouse event</li>
16687         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16688         * to be inserted by setting them on this object.</li>
16689         * <li>cancel - Set this to true to cancel the drop.</li>
16690         * </ul>
16691         * @param {Object} dropEvent
16692         */
16693        "beforenodedrop" : true,
16694        /**
16695         * @event nodedrop
16696         * Fires after a DD object is dropped on a node in this tree. The dropEvent
16697         * passed to handlers has the following properties:<br />
16698         * <ul style="padding:5px;padding-left:16px;">
16699         * <li>tree - The TreePanel</li>
16700         * <li>target - The node being targeted for the drop</li>
16701         * <li>data - The drag data from the drag source</li>
16702         * <li>point - The point of the drop - append, above or below</li>
16703         * <li>source - The drag source</li>
16704         * <li>rawEvent - Raw mouse event</li>
16705         * <li>dropNode - Dropped node(s).</li>
16706         * </ul>
16707         * @param {Object} dropEvent
16708         */
16709        "nodedrop" : true,
16710         /**
16711         * @event nodedragover
16712         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16713         * passed to handlers has the following properties:<br />
16714         * <ul style="padding:5px;padding-left:16px;">
16715         * <li>tree - The TreePanel</li>
16716         * <li>target - The node being targeted for the drop</li>
16717         * <li>data - The drag data from the drag source</li>
16718         * <li>point - The point of the drop - append, above or below</li>
16719         * <li>source - The drag source</li>
16720         * <li>rawEvent - Raw mouse event</li>
16721         * <li>dropNode - Drop node(s) provided by the source.</li>
16722         * <li>cancel - Set this to true to signal drop not allowed.</li>
16723         * </ul>
16724         * @param {Object} dragOverEvent
16725         */
16726        "nodedragover" : true
16727         
16728     });
16729     if(this.singleExpand){
16730        this.on("beforeexpand", this.restrictExpand, this);
16731     }
16732     if (this.editor) {
16733         this.editor.tree = this;
16734         this.editor = Roo.factory(this.editor, Roo.tree);
16735     }
16736     
16737     if (this.selModel) {
16738         this.selModel = Roo.factory(this.selModel, Roo.tree);
16739     }
16740    
16741 };
16742 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16743     rootVisible : true,
16744     animate: Roo.enableFx,
16745     lines : true,
16746     enableDD : false,
16747     hlDrop : Roo.enableFx,
16748   
16749     renderer: false,
16750     
16751     rendererTip: false,
16752     // private
16753     restrictExpand : function(node){
16754         var p = node.parentNode;
16755         if(p){
16756             if(p.expandedChild && p.expandedChild.parentNode == p){
16757                 p.expandedChild.collapse();
16758             }
16759             p.expandedChild = node;
16760         }
16761     },
16762
16763     // private override
16764     setRootNode : function(node){
16765         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16766         if(!this.rootVisible){
16767             node.ui = new Roo.tree.RootTreeNodeUI(node);
16768         }
16769         return node;
16770     },
16771
16772     /**
16773      * Returns the container element for this TreePanel
16774      */
16775     getEl : function(){
16776         return this.el;
16777     },
16778
16779     /**
16780      * Returns the default TreeLoader for this TreePanel
16781      */
16782     getLoader : function(){
16783         return this.loader;
16784     },
16785
16786     /**
16787      * Expand all nodes
16788      */
16789     expandAll : function(){
16790         this.root.expand(true);
16791     },
16792
16793     /**
16794      * Collapse all nodes
16795      */
16796     collapseAll : function(){
16797         this.root.collapse(true);
16798     },
16799
16800     /**
16801      * Returns the selection model used by this TreePanel
16802      */
16803     getSelectionModel : function(){
16804         if(!this.selModel){
16805             this.selModel = new Roo.tree.DefaultSelectionModel();
16806         }
16807         return this.selModel;
16808     },
16809
16810     /**
16811      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16812      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16813      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16814      * @return {Array}
16815      */
16816     getChecked : function(a, startNode){
16817         startNode = startNode || this.root;
16818         var r = [];
16819         var f = function(){
16820             if(this.attributes.checked){
16821                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16822             }
16823         }
16824         startNode.cascade(f);
16825         return r;
16826     },
16827
16828     /**
16829      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16830      * @param {String} path
16831      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16832      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16833      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16834      */
16835     expandPath : function(path, attr, callback){
16836         attr = attr || "id";
16837         var keys = path.split(this.pathSeparator);
16838         var curNode = this.root;
16839         if(curNode.attributes[attr] != keys[1]){ // invalid root
16840             if(callback){
16841                 callback(false, null);
16842             }
16843             return;
16844         }
16845         var index = 1;
16846         var f = function(){
16847             if(++index == keys.length){
16848                 if(callback){
16849                     callback(true, curNode);
16850                 }
16851                 return;
16852             }
16853             var c = curNode.findChild(attr, keys[index]);
16854             if(!c){
16855                 if(callback){
16856                     callback(false, curNode);
16857                 }
16858                 return;
16859             }
16860             curNode = c;
16861             c.expand(false, false, f);
16862         };
16863         curNode.expand(false, false, f);
16864     },
16865
16866     /**
16867      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16868      * @param {String} path
16869      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16870      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16871      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16872      */
16873     selectPath : function(path, attr, callback){
16874         attr = attr || "id";
16875         var keys = path.split(this.pathSeparator);
16876         var v = keys.pop();
16877         if(keys.length > 0){
16878             var f = function(success, node){
16879                 if(success && node){
16880                     var n = node.findChild(attr, v);
16881                     if(n){
16882                         n.select();
16883                         if(callback){
16884                             callback(true, n);
16885                         }
16886                     }else if(callback){
16887                         callback(false, n);
16888                     }
16889                 }else{
16890                     if(callback){
16891                         callback(false, n);
16892                     }
16893                 }
16894             };
16895             this.expandPath(keys.join(this.pathSeparator), attr, f);
16896         }else{
16897             this.root.select();
16898             if(callback){
16899                 callback(true, this.root);
16900             }
16901         }
16902     },
16903
16904     getTreeEl : function(){
16905         return this.el;
16906     },
16907
16908     /**
16909      * Trigger rendering of this TreePanel
16910      */
16911     render : function(){
16912         if (this.innerCt) {
16913             return this; // stop it rendering more than once!!
16914         }
16915         
16916         this.innerCt = this.el.createChild({tag:"ul",
16917                cls:"x-tree-root-ct " +
16918                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16919
16920         if(this.containerScroll){
16921             Roo.dd.ScrollManager.register(this.el);
16922         }
16923         if((this.enableDD || this.enableDrop) && !this.dropZone){
16924            /**
16925             * The dropZone used by this tree if drop is enabled
16926             * @type Roo.tree.TreeDropZone
16927             */
16928              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16929                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16930            });
16931         }
16932         if((this.enableDD || this.enableDrag) && !this.dragZone){
16933            /**
16934             * The dragZone used by this tree if drag is enabled
16935             * @type Roo.tree.TreeDragZone
16936             */
16937             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16938                ddGroup: this.ddGroup || "TreeDD",
16939                scroll: this.ddScroll
16940            });
16941         }
16942         this.getSelectionModel().init(this);
16943         if (!this.root) {
16944             Roo.log("ROOT not set in tree");
16945             return this;
16946         }
16947         this.root.render();
16948         if(!this.rootVisible){
16949             this.root.renderChildren();
16950         }
16951         return this;
16952     }
16953 });/*
16954  * Based on:
16955  * Ext JS Library 1.1.1
16956  * Copyright(c) 2006-2007, Ext JS, LLC.
16957  *
16958  * Originally Released Under LGPL - original licence link has changed is not relivant.
16959  *
16960  * Fork - LGPL
16961  * <script type="text/javascript">
16962  */
16963  
16964
16965 /**
16966  * @class Roo.tree.DefaultSelectionModel
16967  * @extends Roo.util.Observable
16968  * The default single selection for a TreePanel.
16969  * @param {Object} cfg Configuration
16970  */
16971 Roo.tree.DefaultSelectionModel = function(cfg){
16972    this.selNode = null;
16973    
16974    
16975    
16976    this.addEvents({
16977        /**
16978         * @event selectionchange
16979         * Fires when the selected node changes
16980         * @param {DefaultSelectionModel} this
16981         * @param {TreeNode} node the new selection
16982         */
16983        "selectionchange" : true,
16984
16985        /**
16986         * @event beforeselect
16987         * Fires before the selected node changes, return false to cancel the change
16988         * @param {DefaultSelectionModel} this
16989         * @param {TreeNode} node the new selection
16990         * @param {TreeNode} node the old selection
16991         */
16992        "beforeselect" : true
16993    });
16994    
16995     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
16996 };
16997
16998 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16999     init : function(tree){
17000         this.tree = tree;
17001         tree.getTreeEl().on("keydown", this.onKeyDown, this);
17002         tree.on("click", this.onNodeClick, this);
17003     },
17004     
17005     onNodeClick : function(node, e){
17006         if (e.ctrlKey && this.selNode == node)  {
17007             this.unselect(node);
17008             return;
17009         }
17010         this.select(node);
17011     },
17012     
17013     /**
17014      * Select a node.
17015      * @param {TreeNode} node The node to select
17016      * @return {TreeNode} The selected node
17017      */
17018     select : function(node){
17019         var last = this.selNode;
17020         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
17021             if(last){
17022                 last.ui.onSelectedChange(false);
17023             }
17024             this.selNode = node;
17025             node.ui.onSelectedChange(true);
17026             this.fireEvent("selectionchange", this, node, last);
17027         }
17028         return node;
17029     },
17030     
17031     /**
17032      * Deselect a node.
17033      * @param {TreeNode} node The node to unselect
17034      */
17035     unselect : function(node){
17036         if(this.selNode == node){
17037             this.clearSelections();
17038         }    
17039     },
17040     
17041     /**
17042      * Clear all selections
17043      */
17044     clearSelections : function(){
17045         var n = this.selNode;
17046         if(n){
17047             n.ui.onSelectedChange(false);
17048             this.selNode = null;
17049             this.fireEvent("selectionchange", this, null);
17050         }
17051         return n;
17052     },
17053     
17054     /**
17055      * Get the selected node
17056      * @return {TreeNode} The selected node
17057      */
17058     getSelectedNode : function(){
17059         return this.selNode;    
17060     },
17061     
17062     /**
17063      * Returns true if the node is selected
17064      * @param {TreeNode} node The node to check
17065      * @return {Boolean}
17066      */
17067     isSelected : function(node){
17068         return this.selNode == node;  
17069     },
17070
17071     /**
17072      * Selects the node above the selected node in the tree, intelligently walking the nodes
17073      * @return TreeNode The new selection
17074      */
17075     selectPrevious : function(){
17076         var s = this.selNode || this.lastSelNode;
17077         if(!s){
17078             return null;
17079         }
17080         var ps = s.previousSibling;
17081         if(ps){
17082             if(!ps.isExpanded() || ps.childNodes.length < 1){
17083                 return this.select(ps);
17084             } else{
17085                 var lc = ps.lastChild;
17086                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
17087                     lc = lc.lastChild;
17088                 }
17089                 return this.select(lc);
17090             }
17091         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
17092             return this.select(s.parentNode);
17093         }
17094         return null;
17095     },
17096
17097     /**
17098      * Selects the node above the selected node in the tree, intelligently walking the nodes
17099      * @return TreeNode The new selection
17100      */
17101     selectNext : function(){
17102         var s = this.selNode || this.lastSelNode;
17103         if(!s){
17104             return null;
17105         }
17106         if(s.firstChild && s.isExpanded()){
17107              return this.select(s.firstChild);
17108          }else if(s.nextSibling){
17109              return this.select(s.nextSibling);
17110          }else if(s.parentNode){
17111             var newS = null;
17112             s.parentNode.bubble(function(){
17113                 if(this.nextSibling){
17114                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
17115                     return false;
17116                 }
17117             });
17118             return newS;
17119          }
17120         return null;
17121     },
17122
17123     onKeyDown : function(e){
17124         var s = this.selNode || this.lastSelNode;
17125         // undesirable, but required
17126         var sm = this;
17127         if(!s){
17128             return;
17129         }
17130         var k = e.getKey();
17131         switch(k){
17132              case e.DOWN:
17133                  e.stopEvent();
17134                  this.selectNext();
17135              break;
17136              case e.UP:
17137                  e.stopEvent();
17138                  this.selectPrevious();
17139              break;
17140              case e.RIGHT:
17141                  e.preventDefault();
17142                  if(s.hasChildNodes()){
17143                      if(!s.isExpanded()){
17144                          s.expand();
17145                      }else if(s.firstChild){
17146                          this.select(s.firstChild, e);
17147                      }
17148                  }
17149              break;
17150              case e.LEFT:
17151                  e.preventDefault();
17152                  if(s.hasChildNodes() && s.isExpanded()){
17153                      s.collapse();
17154                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
17155                      this.select(s.parentNode, e);
17156                  }
17157              break;
17158         };
17159     }
17160 });
17161
17162 /**
17163  * @class Roo.tree.MultiSelectionModel
17164  * @extends Roo.util.Observable
17165  * Multi selection for a TreePanel.
17166  * @param {Object} cfg Configuration
17167  */
17168 Roo.tree.MultiSelectionModel = function(){
17169    this.selNodes = [];
17170    this.selMap = {};
17171    this.addEvents({
17172        /**
17173         * @event selectionchange
17174         * Fires when the selected nodes change
17175         * @param {MultiSelectionModel} this
17176         * @param {Array} nodes Array of the selected nodes
17177         */
17178        "selectionchange" : true
17179    });
17180    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
17181    
17182 };
17183
17184 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
17185     init : function(tree){
17186         this.tree = tree;
17187         tree.getTreeEl().on("keydown", this.onKeyDown, this);
17188         tree.on("click", this.onNodeClick, this);
17189     },
17190     
17191     onNodeClick : function(node, e){
17192         this.select(node, e, e.ctrlKey);
17193     },
17194     
17195     /**
17196      * Select a node.
17197      * @param {TreeNode} node The node to select
17198      * @param {EventObject} e (optional) An event associated with the selection
17199      * @param {Boolean} keepExisting True to retain existing selections
17200      * @return {TreeNode} The selected node
17201      */
17202     select : function(node, e, keepExisting){
17203         if(keepExisting !== true){
17204             this.clearSelections(true);
17205         }
17206         if(this.isSelected(node)){
17207             this.lastSelNode = node;
17208             return node;
17209         }
17210         this.selNodes.push(node);
17211         this.selMap[node.id] = node;
17212         this.lastSelNode = node;
17213         node.ui.onSelectedChange(true);
17214         this.fireEvent("selectionchange", this, this.selNodes);
17215         return node;
17216     },
17217     
17218     /**
17219      * Deselect a node.
17220      * @param {TreeNode} node The node to unselect
17221      */
17222     unselect : function(node){
17223         if(this.selMap[node.id]){
17224             node.ui.onSelectedChange(false);
17225             var sn = this.selNodes;
17226             var index = -1;
17227             if(sn.indexOf){
17228                 index = sn.indexOf(node);
17229             }else{
17230                 for(var i = 0, len = sn.length; i < len; i++){
17231                     if(sn[i] == node){
17232                         index = i;
17233                         break;
17234                     }
17235                 }
17236             }
17237             if(index != -1){
17238                 this.selNodes.splice(index, 1);
17239             }
17240             delete this.selMap[node.id];
17241             this.fireEvent("selectionchange", this, this.selNodes);
17242         }
17243     },
17244     
17245     /**
17246      * Clear all selections
17247      */
17248     clearSelections : function(suppressEvent){
17249         var sn = this.selNodes;
17250         if(sn.length > 0){
17251             for(var i = 0, len = sn.length; i < len; i++){
17252                 sn[i].ui.onSelectedChange(false);
17253             }
17254             this.selNodes = [];
17255             this.selMap = {};
17256             if(suppressEvent !== true){
17257                 this.fireEvent("selectionchange", this, this.selNodes);
17258             }
17259         }
17260     },
17261     
17262     /**
17263      * Returns true if the node is selected
17264      * @param {TreeNode} node The node to check
17265      * @return {Boolean}
17266      */
17267     isSelected : function(node){
17268         return this.selMap[node.id] ? true : false;  
17269     },
17270     
17271     /**
17272      * Returns an array of the selected nodes
17273      * @return {Array}
17274      */
17275     getSelectedNodes : function(){
17276         return this.selNodes;    
17277     },
17278
17279     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17280
17281     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17282
17283     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17284 });/*
17285  * Based on:
17286  * Ext JS Library 1.1.1
17287  * Copyright(c) 2006-2007, Ext JS, LLC.
17288  *
17289  * Originally Released Under LGPL - original licence link has changed is not relivant.
17290  *
17291  * Fork - LGPL
17292  * <script type="text/javascript">
17293  */
17294  
17295 /**
17296  * @class Roo.tree.TreeNode
17297  * @extends Roo.data.Node
17298  * @cfg {String} text The text for this node
17299  * @cfg {Boolean} expanded true to start the node expanded
17300  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17301  * @cfg {Boolean} allowDrop false if this node cannot be drop on
17302  * @cfg {Boolean} disabled true to start the node disabled
17303  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17304  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17305  * @cfg {String} cls A css class to be added to the node
17306  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17307  * @cfg {String} href URL of the link used for the node (defaults to #)
17308  * @cfg {String} hrefTarget target frame for the link
17309  * @cfg {String} qtip An Ext QuickTip for the node
17310  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17311  * @cfg {Boolean} singleClickExpand True for single click expand on this node
17312  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17313  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17314  * (defaults to undefined with no checkbox rendered)
17315  * @constructor
17316  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17317  */
17318 Roo.tree.TreeNode = function(attributes){
17319     attributes = attributes || {};
17320     if(typeof attributes == "string"){
17321         attributes = {text: attributes};
17322     }
17323     this.childrenRendered = false;
17324     this.rendered = false;
17325     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17326     this.expanded = attributes.expanded === true;
17327     this.isTarget = attributes.isTarget !== false;
17328     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17329     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17330
17331     /**
17332      * Read-only. The text for this node. To change it use setText().
17333      * @type String
17334      */
17335     this.text = attributes.text;
17336     /**
17337      * True if this node is disabled.
17338      * @type Boolean
17339      */
17340     this.disabled = attributes.disabled === true;
17341
17342     this.addEvents({
17343         /**
17344         * @event textchange
17345         * Fires when the text for this node is changed
17346         * @param {Node} this This node
17347         * @param {String} text The new text
17348         * @param {String} oldText The old text
17349         */
17350         "textchange" : true,
17351         /**
17352         * @event beforeexpand
17353         * Fires before this node is expanded, return false to cancel.
17354         * @param {Node} this This node
17355         * @param {Boolean} deep
17356         * @param {Boolean} anim
17357         */
17358         "beforeexpand" : true,
17359         /**
17360         * @event beforecollapse
17361         * Fires before this node is collapsed, return false to cancel.
17362         * @param {Node} this This node
17363         * @param {Boolean} deep
17364         * @param {Boolean} anim
17365         */
17366         "beforecollapse" : true,
17367         /**
17368         * @event expand
17369         * Fires when this node is expanded
17370         * @param {Node} this This node
17371         */
17372         "expand" : true,
17373         /**
17374         * @event disabledchange
17375         * Fires when the disabled status of this node changes
17376         * @param {Node} this This node
17377         * @param {Boolean} disabled
17378         */
17379         "disabledchange" : true,
17380         /**
17381         * @event collapse
17382         * Fires when this node is collapsed
17383         * @param {Node} this This node
17384         */
17385         "collapse" : true,
17386         /**
17387         * @event beforeclick
17388         * Fires before click processing. Return false to cancel the default action.
17389         * @param {Node} this This node
17390         * @param {Roo.EventObject} e The event object
17391         */
17392         "beforeclick":true,
17393         /**
17394         * @event checkchange
17395         * Fires when a node with a checkbox's checked property changes
17396         * @param {Node} this This node
17397         * @param {Boolean} checked
17398         */
17399         "checkchange":true,
17400         /**
17401         * @event click
17402         * Fires when this node is clicked
17403         * @param {Node} this This node
17404         * @param {Roo.EventObject} e The event object
17405         */
17406         "click":true,
17407         /**
17408         * @event dblclick
17409         * Fires when this node is double clicked
17410         * @param {Node} this This node
17411         * @param {Roo.EventObject} e The event object
17412         */
17413         "dblclick":true,
17414         /**
17415         * @event contextmenu
17416         * Fires when this node is right clicked
17417         * @param {Node} this This node
17418         * @param {Roo.EventObject} e The event object
17419         */
17420         "contextmenu":true,
17421         /**
17422         * @event beforechildrenrendered
17423         * Fires right before the child nodes for this node are rendered
17424         * @param {Node} this This node
17425         */
17426         "beforechildrenrendered":true
17427     });
17428
17429     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17430
17431     /**
17432      * Read-only. The UI for this node
17433      * @type TreeNodeUI
17434      */
17435     this.ui = new uiClass(this);
17436     
17437     // finally support items[]
17438     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
17439         return;
17440     }
17441     
17442     
17443     Roo.each(this.attributes.items, function(c) {
17444         this.appendChild(Roo.factory(c,Roo.Tree));
17445     }, this);
17446     delete this.attributes.items;
17447     
17448     
17449     
17450 };
17451 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17452     preventHScroll: true,
17453     /**
17454      * Returns true if this node is expanded
17455      * @return {Boolean}
17456      */
17457     isExpanded : function(){
17458         return this.expanded;
17459     },
17460
17461     /**
17462      * Returns the UI object for this node
17463      * @return {TreeNodeUI}
17464      */
17465     getUI : function(){
17466         return this.ui;
17467     },
17468
17469     // private override
17470     setFirstChild : function(node){
17471         var of = this.firstChild;
17472         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17473         if(this.childrenRendered && of && node != of){
17474             of.renderIndent(true, true);
17475         }
17476         if(this.rendered){
17477             this.renderIndent(true, true);
17478         }
17479     },
17480
17481     // private override
17482     setLastChild : function(node){
17483         var ol = this.lastChild;
17484         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17485         if(this.childrenRendered && ol && node != ol){
17486             ol.renderIndent(true, true);
17487         }
17488         if(this.rendered){
17489             this.renderIndent(true, true);
17490         }
17491     },
17492
17493     // these methods are overridden to provide lazy rendering support
17494     // private override
17495     appendChild : function()
17496     {
17497         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17498         if(node && this.childrenRendered){
17499             node.render();
17500         }
17501         this.ui.updateExpandIcon();
17502         return node;
17503     },
17504
17505     // private override
17506     removeChild : function(node){
17507         this.ownerTree.getSelectionModel().unselect(node);
17508         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17509         // if it's been rendered remove dom node
17510         if(this.childrenRendered){
17511             node.ui.remove();
17512         }
17513         if(this.childNodes.length < 1){
17514             this.collapse(false, false);
17515         }else{
17516             this.ui.updateExpandIcon();
17517         }
17518         if(!this.firstChild) {
17519             this.childrenRendered = false;
17520         }
17521         return node;
17522     },
17523
17524     // private override
17525     insertBefore : function(node, refNode){
17526         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17527         if(newNode && refNode && this.childrenRendered){
17528             node.render();
17529         }
17530         this.ui.updateExpandIcon();
17531         return newNode;
17532     },
17533
17534     /**
17535      * Sets the text for this node
17536      * @param {String} text
17537      */
17538     setText : function(text){
17539         var oldText = this.text;
17540         this.text = text;
17541         this.attributes.text = text;
17542         if(this.rendered){ // event without subscribing
17543             this.ui.onTextChange(this, text, oldText);
17544         }
17545         this.fireEvent("textchange", this, text, oldText);
17546     },
17547
17548     /**
17549      * Triggers selection of this node
17550      */
17551     select : function(){
17552         this.getOwnerTree().getSelectionModel().select(this);
17553     },
17554
17555     /**
17556      * Triggers deselection of this node
17557      */
17558     unselect : function(){
17559         this.getOwnerTree().getSelectionModel().unselect(this);
17560     },
17561
17562     /**
17563      * Returns true if this node is selected
17564      * @return {Boolean}
17565      */
17566     isSelected : function(){
17567         return this.getOwnerTree().getSelectionModel().isSelected(this);
17568     },
17569
17570     /**
17571      * Expand this node.
17572      * @param {Boolean} deep (optional) True to expand all children as well
17573      * @param {Boolean} anim (optional) false to cancel the default animation
17574      * @param {Function} callback (optional) A callback to be called when
17575      * expanding this node completes (does not wait for deep expand to complete).
17576      * Called with 1 parameter, this node.
17577      */
17578     expand : function(deep, anim, callback){
17579         if(!this.expanded){
17580             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17581                 return;
17582             }
17583             if(!this.childrenRendered){
17584                 this.renderChildren();
17585             }
17586             this.expanded = true;
17587             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17588                 this.ui.animExpand(function(){
17589                     this.fireEvent("expand", this);
17590                     if(typeof callback == "function"){
17591                         callback(this);
17592                     }
17593                     if(deep === true){
17594                         this.expandChildNodes(true);
17595                     }
17596                 }.createDelegate(this));
17597                 return;
17598             }else{
17599                 this.ui.expand();
17600                 this.fireEvent("expand", this);
17601                 if(typeof callback == "function"){
17602                     callback(this);
17603                 }
17604             }
17605         }else{
17606            if(typeof callback == "function"){
17607                callback(this);
17608            }
17609         }
17610         if(deep === true){
17611             this.expandChildNodes(true);
17612         }
17613     },
17614
17615     isHiddenRoot : function(){
17616         return this.isRoot && !this.getOwnerTree().rootVisible;
17617     },
17618
17619     /**
17620      * Collapse this node.
17621      * @param {Boolean} deep (optional) True to collapse all children as well
17622      * @param {Boolean} anim (optional) false to cancel the default animation
17623      */
17624     collapse : function(deep, anim){
17625         if(this.expanded && !this.isHiddenRoot()){
17626             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17627                 return;
17628             }
17629             this.expanded = false;
17630             if((this.getOwnerTree().animate && anim !== false) || anim){
17631                 this.ui.animCollapse(function(){
17632                     this.fireEvent("collapse", this);
17633                     if(deep === true){
17634                         this.collapseChildNodes(true);
17635                     }
17636                 }.createDelegate(this));
17637                 return;
17638             }else{
17639                 this.ui.collapse();
17640                 this.fireEvent("collapse", this);
17641             }
17642         }
17643         if(deep === true){
17644             var cs = this.childNodes;
17645             for(var i = 0, len = cs.length; i < len; i++) {
17646                 cs[i].collapse(true, false);
17647             }
17648         }
17649     },
17650
17651     // private
17652     delayedExpand : function(delay){
17653         if(!this.expandProcId){
17654             this.expandProcId = this.expand.defer(delay, this);
17655         }
17656     },
17657
17658     // private
17659     cancelExpand : function(){
17660         if(this.expandProcId){
17661             clearTimeout(this.expandProcId);
17662         }
17663         this.expandProcId = false;
17664     },
17665
17666     /**
17667      * Toggles expanded/collapsed state of the node
17668      */
17669     toggle : function(){
17670         if(this.expanded){
17671             this.collapse();
17672         }else{
17673             this.expand();
17674         }
17675     },
17676
17677     /**
17678      * Ensures all parent nodes are expanded
17679      */
17680     ensureVisible : function(callback){
17681         var tree = this.getOwnerTree();
17682         tree.expandPath(this.parentNode.getPath(), false, function(){
17683             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17684             Roo.callback(callback);
17685         }.createDelegate(this));
17686     },
17687
17688     /**
17689      * Expand all child nodes
17690      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17691      */
17692     expandChildNodes : function(deep){
17693         var cs = this.childNodes;
17694         for(var i = 0, len = cs.length; i < len; i++) {
17695                 cs[i].expand(deep);
17696         }
17697     },
17698
17699     /**
17700      * Collapse all child nodes
17701      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17702      */
17703     collapseChildNodes : function(deep){
17704         var cs = this.childNodes;
17705         for(var i = 0, len = cs.length; i < len; i++) {
17706                 cs[i].collapse(deep);
17707         }
17708     },
17709
17710     /**
17711      * Disables this node
17712      */
17713     disable : function(){
17714         this.disabled = true;
17715         this.unselect();
17716         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17717             this.ui.onDisableChange(this, true);
17718         }
17719         this.fireEvent("disabledchange", this, true);
17720     },
17721
17722     /**
17723      * Enables this node
17724      */
17725     enable : function(){
17726         this.disabled = false;
17727         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17728             this.ui.onDisableChange(this, false);
17729         }
17730         this.fireEvent("disabledchange", this, false);
17731     },
17732
17733     // private
17734     renderChildren : function(suppressEvent){
17735         if(suppressEvent !== false){
17736             this.fireEvent("beforechildrenrendered", this);
17737         }
17738         var cs = this.childNodes;
17739         for(var i = 0, len = cs.length; i < len; i++){
17740             cs[i].render(true);
17741         }
17742         this.childrenRendered = true;
17743     },
17744
17745     // private
17746     sort : function(fn, scope){
17747         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17748         if(this.childrenRendered){
17749             var cs = this.childNodes;
17750             for(var i = 0, len = cs.length; i < len; i++){
17751                 cs[i].render(true);
17752             }
17753         }
17754     },
17755
17756     // private
17757     render : function(bulkRender){
17758         this.ui.render(bulkRender);
17759         if(!this.rendered){
17760             this.rendered = true;
17761             if(this.expanded){
17762                 this.expanded = false;
17763                 this.expand(false, false);
17764             }
17765         }
17766     },
17767
17768     // private
17769     renderIndent : function(deep, refresh){
17770         if(refresh){
17771             this.ui.childIndent = null;
17772         }
17773         this.ui.renderIndent();
17774         if(deep === true && this.childrenRendered){
17775             var cs = this.childNodes;
17776             for(var i = 0, len = cs.length; i < len; i++){
17777                 cs[i].renderIndent(true, refresh);
17778             }
17779         }
17780     }
17781 });/*
17782  * Based on:
17783  * Ext JS Library 1.1.1
17784  * Copyright(c) 2006-2007, Ext JS, LLC.
17785  *
17786  * Originally Released Under LGPL - original licence link has changed is not relivant.
17787  *
17788  * Fork - LGPL
17789  * <script type="text/javascript">
17790  */
17791  
17792 /**
17793  * @class Roo.tree.AsyncTreeNode
17794  * @extends Roo.tree.TreeNode
17795  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17796  * @constructor
17797  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17798  */
17799  Roo.tree.AsyncTreeNode = function(config){
17800     this.loaded = false;
17801     this.loading = false;
17802     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17803     /**
17804     * @event beforeload
17805     * Fires before this node is loaded, return false to cancel
17806     * @param {Node} this This node
17807     */
17808     this.addEvents({'beforeload':true, 'load': true});
17809     /**
17810     * @event load
17811     * Fires when this node is loaded
17812     * @param {Node} this This node
17813     */
17814     /**
17815      * The loader used by this node (defaults to using the tree's defined loader)
17816      * @type TreeLoader
17817      * @property loader
17818      */
17819 };
17820 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17821     expand : function(deep, anim, callback){
17822         if(this.loading){ // if an async load is already running, waiting til it's done
17823             var timer;
17824             var f = function(){
17825                 if(!this.loading){ // done loading
17826                     clearInterval(timer);
17827                     this.expand(deep, anim, callback);
17828                 }
17829             }.createDelegate(this);
17830             timer = setInterval(f, 200);
17831             return;
17832         }
17833         if(!this.loaded){
17834             if(this.fireEvent("beforeload", this) === false){
17835                 return;
17836             }
17837             this.loading = true;
17838             this.ui.beforeLoad(this);
17839             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17840             if(loader){
17841                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17842                 return;
17843             }
17844         }
17845         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17846     },
17847     
17848     /**
17849      * Returns true if this node is currently loading
17850      * @return {Boolean}
17851      */
17852     isLoading : function(){
17853         return this.loading;  
17854     },
17855     
17856     loadComplete : function(deep, anim, callback){
17857         this.loading = false;
17858         this.loaded = true;
17859         this.ui.afterLoad(this);
17860         this.fireEvent("load", this);
17861         this.expand(deep, anim, callback);
17862     },
17863     
17864     /**
17865      * Returns true if this node has been loaded
17866      * @return {Boolean}
17867      */
17868     isLoaded : function(){
17869         return this.loaded;
17870     },
17871     
17872     hasChildNodes : function(){
17873         if(!this.isLeaf() && !this.loaded){
17874             return true;
17875         }else{
17876             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17877         }
17878     },
17879
17880     /**
17881      * Trigger a reload for this node
17882      * @param {Function} callback
17883      */
17884     reload : function(callback){
17885         this.collapse(false, false);
17886         while(this.firstChild){
17887             this.removeChild(this.firstChild);
17888         }
17889         this.childrenRendered = false;
17890         this.loaded = false;
17891         if(this.isHiddenRoot()){
17892             this.expanded = false;
17893         }
17894         this.expand(false, false, callback);
17895     }
17896 });/*
17897  * Based on:
17898  * Ext JS Library 1.1.1
17899  * Copyright(c) 2006-2007, Ext JS, LLC.
17900  *
17901  * Originally Released Under LGPL - original licence link has changed is not relivant.
17902  *
17903  * Fork - LGPL
17904  * <script type="text/javascript">
17905  */
17906  
17907 /**
17908  * @class Roo.tree.TreeNodeUI
17909  * @constructor
17910  * @param {Object} node The node to render
17911  * The TreeNode UI implementation is separate from the
17912  * tree implementation. Unless you are customizing the tree UI,
17913  * you should never have to use this directly.
17914  */
17915 Roo.tree.TreeNodeUI = function(node){
17916     this.node = node;
17917     this.rendered = false;
17918     this.animating = false;
17919     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17920 };
17921
17922 Roo.tree.TreeNodeUI.prototype = {
17923     removeChild : function(node){
17924         if(this.rendered){
17925             this.ctNode.removeChild(node.ui.getEl());
17926         }
17927     },
17928
17929     beforeLoad : function(){
17930          this.addClass("x-tree-node-loading");
17931     },
17932
17933     afterLoad : function(){
17934          this.removeClass("x-tree-node-loading");
17935     },
17936
17937     onTextChange : function(node, text, oldText){
17938         if(this.rendered){
17939             this.textNode.innerHTML = text;
17940         }
17941     },
17942
17943     onDisableChange : function(node, state){
17944         this.disabled = state;
17945         if(state){
17946             this.addClass("x-tree-node-disabled");
17947         }else{
17948             this.removeClass("x-tree-node-disabled");
17949         }
17950     },
17951
17952     onSelectedChange : function(state){
17953         if(state){
17954             this.focus();
17955             this.addClass("x-tree-selected");
17956         }else{
17957             //this.blur();
17958             this.removeClass("x-tree-selected");
17959         }
17960     },
17961
17962     onMove : function(tree, node, oldParent, newParent, index, refNode){
17963         this.childIndent = null;
17964         if(this.rendered){
17965             var targetNode = newParent.ui.getContainer();
17966             if(!targetNode){//target not rendered
17967                 this.holder = document.createElement("div");
17968                 this.holder.appendChild(this.wrap);
17969                 return;
17970             }
17971             var insertBefore = refNode ? refNode.ui.getEl() : null;
17972             if(insertBefore){
17973                 targetNode.insertBefore(this.wrap, insertBefore);
17974             }else{
17975                 targetNode.appendChild(this.wrap);
17976             }
17977             this.node.renderIndent(true);
17978         }
17979     },
17980
17981     addClass : function(cls){
17982         if(this.elNode){
17983             Roo.fly(this.elNode).addClass(cls);
17984         }
17985     },
17986
17987     removeClass : function(cls){
17988         if(this.elNode){
17989             Roo.fly(this.elNode).removeClass(cls);
17990         }
17991     },
17992
17993     remove : function(){
17994         if(this.rendered){
17995             this.holder = document.createElement("div");
17996             this.holder.appendChild(this.wrap);
17997         }
17998     },
17999
18000     fireEvent : function(){
18001         return this.node.fireEvent.apply(this.node, arguments);
18002     },
18003
18004     initEvents : function(){
18005         this.node.on("move", this.onMove, this);
18006         var E = Roo.EventManager;
18007         var a = this.anchor;
18008
18009         var el = Roo.fly(a, '_treeui');
18010
18011         if(Roo.isOpera){ // opera render bug ignores the CSS
18012             el.setStyle("text-decoration", "none");
18013         }
18014
18015         el.on("click", this.onClick, this);
18016         el.on("dblclick", this.onDblClick, this);
18017
18018         if(this.checkbox){
18019             Roo.EventManager.on(this.checkbox,
18020                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
18021         }
18022
18023         el.on("contextmenu", this.onContextMenu, this);
18024
18025         var icon = Roo.fly(this.iconNode);
18026         icon.on("click", this.onClick, this);
18027         icon.on("dblclick", this.onDblClick, this);
18028         icon.on("contextmenu", this.onContextMenu, this);
18029         E.on(this.ecNode, "click", this.ecClick, this, true);
18030
18031         if(this.node.disabled){
18032             this.addClass("x-tree-node-disabled");
18033         }
18034         if(this.node.hidden){
18035             this.addClass("x-tree-node-disabled");
18036         }
18037         var ot = this.node.getOwnerTree();
18038         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
18039         if(dd && (!this.node.isRoot || ot.rootVisible)){
18040             Roo.dd.Registry.register(this.elNode, {
18041                 node: this.node,
18042                 handles: this.getDDHandles(),
18043                 isHandle: false
18044             });
18045         }
18046     },
18047
18048     getDDHandles : function(){
18049         return [this.iconNode, this.textNode];
18050     },
18051
18052     hide : function(){
18053         if(this.rendered){
18054             this.wrap.style.display = "none";
18055         }
18056     },
18057
18058     show : function(){
18059         if(this.rendered){
18060             this.wrap.style.display = "";
18061         }
18062     },
18063
18064     onContextMenu : function(e){
18065         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
18066             e.preventDefault();
18067             this.focus();
18068             this.fireEvent("contextmenu", this.node, e);
18069         }
18070     },
18071
18072     onClick : function(e){
18073         if(this.dropping){
18074             e.stopEvent();
18075             return;
18076         }
18077         if(this.fireEvent("beforeclick", this.node, e) !== false){
18078             if(!this.disabled && this.node.attributes.href){
18079                 this.fireEvent("click", this.node, e);
18080                 return;
18081             }
18082             e.preventDefault();
18083             if(this.disabled){
18084                 return;
18085             }
18086
18087             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
18088                 this.node.toggle();
18089             }
18090
18091             this.fireEvent("click", this.node, e);
18092         }else{
18093             e.stopEvent();
18094         }
18095     },
18096
18097     onDblClick : function(e){
18098         e.preventDefault();
18099         if(this.disabled){
18100             return;
18101         }
18102         if(this.checkbox){
18103             this.toggleCheck();
18104         }
18105         if(!this.animating && this.node.hasChildNodes()){
18106             this.node.toggle();
18107         }
18108         this.fireEvent("dblclick", this.node, e);
18109     },
18110
18111     onCheckChange : function(){
18112         var checked = this.checkbox.checked;
18113         this.node.attributes.checked = checked;
18114         this.fireEvent('checkchange', this.node, checked);
18115     },
18116
18117     ecClick : function(e){
18118         if(!this.animating && this.node.hasChildNodes()){
18119             this.node.toggle();
18120         }
18121     },
18122
18123     startDrop : function(){
18124         this.dropping = true;
18125     },
18126
18127     // delayed drop so the click event doesn't get fired on a drop
18128     endDrop : function(){
18129        setTimeout(function(){
18130            this.dropping = false;
18131        }.createDelegate(this), 50);
18132     },
18133
18134     expand : function(){
18135         this.updateExpandIcon();
18136         this.ctNode.style.display = "";
18137     },
18138
18139     focus : function(){
18140         if(!this.node.preventHScroll){
18141             try{this.anchor.focus();
18142             }catch(e){}
18143         }else if(!Roo.isIE){
18144             try{
18145                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
18146                 var l = noscroll.scrollLeft;
18147                 this.anchor.focus();
18148                 noscroll.scrollLeft = l;
18149             }catch(e){}
18150         }
18151     },
18152
18153     toggleCheck : function(value){
18154         var cb = this.checkbox;
18155         if(cb){
18156             cb.checked = (value === undefined ? !cb.checked : value);
18157         }
18158     },
18159
18160     blur : function(){
18161         try{
18162             this.anchor.blur();
18163         }catch(e){}
18164     },
18165
18166     animExpand : function(callback){
18167         var ct = Roo.get(this.ctNode);
18168         ct.stopFx();
18169         if(!this.node.hasChildNodes()){
18170             this.updateExpandIcon();
18171             this.ctNode.style.display = "";
18172             Roo.callback(callback);
18173             return;
18174         }
18175         this.animating = true;
18176         this.updateExpandIcon();
18177
18178         ct.slideIn('t', {
18179            callback : function(){
18180                this.animating = false;
18181                Roo.callback(callback);
18182             },
18183             scope: this,
18184             duration: this.node.ownerTree.duration || .25
18185         });
18186     },
18187
18188     highlight : function(){
18189         var tree = this.node.getOwnerTree();
18190         Roo.fly(this.wrap).highlight(
18191             tree.hlColor || "C3DAF9",
18192             {endColor: tree.hlBaseColor}
18193         );
18194     },
18195
18196     collapse : function(){
18197         this.updateExpandIcon();
18198         this.ctNode.style.display = "none";
18199     },
18200
18201     animCollapse : function(callback){
18202         var ct = Roo.get(this.ctNode);
18203         ct.enableDisplayMode('block');
18204         ct.stopFx();
18205
18206         this.animating = true;
18207         this.updateExpandIcon();
18208
18209         ct.slideOut('t', {
18210             callback : function(){
18211                this.animating = false;
18212                Roo.callback(callback);
18213             },
18214             scope: this,
18215             duration: this.node.ownerTree.duration || .25
18216         });
18217     },
18218
18219     getContainer : function(){
18220         return this.ctNode;
18221     },
18222
18223     getEl : function(){
18224         return this.wrap;
18225     },
18226
18227     appendDDGhost : function(ghostNode){
18228         ghostNode.appendChild(this.elNode.cloneNode(true));
18229     },
18230
18231     getDDRepairXY : function(){
18232         return Roo.lib.Dom.getXY(this.iconNode);
18233     },
18234
18235     onRender : function(){
18236         this.render();
18237     },
18238
18239     render : function(bulkRender){
18240         var n = this.node, a = n.attributes;
18241         var targetNode = n.parentNode ?
18242               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
18243
18244         if(!this.rendered){
18245             this.rendered = true;
18246
18247             this.renderElements(n, a, targetNode, bulkRender);
18248
18249             if(a.qtip){
18250                if(this.textNode.setAttributeNS){
18251                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
18252                    if(a.qtipTitle){
18253                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
18254                    }
18255                }else{
18256                    this.textNode.setAttribute("ext:qtip", a.qtip);
18257                    if(a.qtipTitle){
18258                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
18259                    }
18260                }
18261             }else if(a.qtipCfg){
18262                 a.qtipCfg.target = Roo.id(this.textNode);
18263                 Roo.QuickTips.register(a.qtipCfg);
18264             }
18265             this.initEvents();
18266             if(!this.node.expanded){
18267                 this.updateExpandIcon();
18268             }
18269         }else{
18270             if(bulkRender === true) {
18271                 targetNode.appendChild(this.wrap);
18272             }
18273         }
18274     },
18275
18276     renderElements : function(n, a, targetNode, bulkRender)
18277     {
18278         // add some indent caching, this helps performance when rendering a large tree
18279         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18280         var t = n.getOwnerTree();
18281         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18282         if (typeof(n.attributes.html) != 'undefined') {
18283             txt = n.attributes.html;
18284         }
18285         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18286         var cb = typeof a.checked == 'boolean';
18287         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18288         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18289             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18290             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18291             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18292             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18293             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18294              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
18295                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18296             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18297             "</li>"];
18298
18299         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18300             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18301                                 n.nextSibling.ui.getEl(), buf.join(""));
18302         }else{
18303             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18304         }
18305
18306         this.elNode = this.wrap.childNodes[0];
18307         this.ctNode = this.wrap.childNodes[1];
18308         var cs = this.elNode.childNodes;
18309         this.indentNode = cs[0];
18310         this.ecNode = cs[1];
18311         this.iconNode = cs[2];
18312         var index = 3;
18313         if(cb){
18314             this.checkbox = cs[3];
18315             index++;
18316         }
18317         this.anchor = cs[index];
18318         this.textNode = cs[index].firstChild;
18319     },
18320
18321     getAnchor : function(){
18322         return this.anchor;
18323     },
18324
18325     getTextEl : function(){
18326         return this.textNode;
18327     },
18328
18329     getIconEl : function(){
18330         return this.iconNode;
18331     },
18332
18333     isChecked : function(){
18334         return this.checkbox ? this.checkbox.checked : false;
18335     },
18336
18337     updateExpandIcon : function(){
18338         if(this.rendered){
18339             var n = this.node, c1, c2;
18340             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18341             var hasChild = n.hasChildNodes();
18342             if(hasChild){
18343                 if(n.expanded){
18344                     cls += "-minus";
18345                     c1 = "x-tree-node-collapsed";
18346                     c2 = "x-tree-node-expanded";
18347                 }else{
18348                     cls += "-plus";
18349                     c1 = "x-tree-node-expanded";
18350                     c2 = "x-tree-node-collapsed";
18351                 }
18352                 if(this.wasLeaf){
18353                     this.removeClass("x-tree-node-leaf");
18354                     this.wasLeaf = false;
18355                 }
18356                 if(this.c1 != c1 || this.c2 != c2){
18357                     Roo.fly(this.elNode).replaceClass(c1, c2);
18358                     this.c1 = c1; this.c2 = c2;
18359                 }
18360             }else{
18361                 // this changes non-leafs into leafs if they have no children.
18362                 // it's not very rational behaviour..
18363                 
18364                 if(!this.wasLeaf && this.node.leaf){
18365                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18366                     delete this.c1;
18367                     delete this.c2;
18368                     this.wasLeaf = true;
18369                 }
18370             }
18371             var ecc = "x-tree-ec-icon "+cls;
18372             if(this.ecc != ecc){
18373                 this.ecNode.className = ecc;
18374                 this.ecc = ecc;
18375             }
18376         }
18377     },
18378
18379     getChildIndent : function(){
18380         if(!this.childIndent){
18381             var buf = [];
18382             var p = this.node;
18383             while(p){
18384                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18385                     if(!p.isLast()) {
18386                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18387                     } else {
18388                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18389                     }
18390                 }
18391                 p = p.parentNode;
18392             }
18393             this.childIndent = buf.join("");
18394         }
18395         return this.childIndent;
18396     },
18397
18398     renderIndent : function(){
18399         if(this.rendered){
18400             var indent = "";
18401             var p = this.node.parentNode;
18402             if(p){
18403                 indent = p.ui.getChildIndent();
18404             }
18405             if(this.indentMarkup != indent){ // don't rerender if not required
18406                 this.indentNode.innerHTML = indent;
18407                 this.indentMarkup = indent;
18408             }
18409             this.updateExpandIcon();
18410         }
18411     }
18412 };
18413
18414 Roo.tree.RootTreeNodeUI = function(){
18415     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18416 };
18417 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18418     render : function(){
18419         if(!this.rendered){
18420             var targetNode = this.node.ownerTree.innerCt.dom;
18421             this.node.expanded = true;
18422             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18423             this.wrap = this.ctNode = targetNode.firstChild;
18424         }
18425     },
18426     collapse : function(){
18427     },
18428     expand : function(){
18429     }
18430 });/*
18431  * Based on:
18432  * Ext JS Library 1.1.1
18433  * Copyright(c) 2006-2007, Ext JS, LLC.
18434  *
18435  * Originally Released Under LGPL - original licence link has changed is not relivant.
18436  *
18437  * Fork - LGPL
18438  * <script type="text/javascript">
18439  */
18440 /**
18441  * @class Roo.tree.TreeLoader
18442  * @extends Roo.util.Observable
18443  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18444  * nodes from a specified URL. The response must be a javascript Array definition
18445  * who's elements are node definition objects. eg:
18446  * <pre><code>
18447 {  success : true,
18448    data :      [
18449    
18450     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
18451     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
18452     ]
18453 }
18454
18455
18456 </code></pre>
18457  * <br><br>
18458  * The old style respose with just an array is still supported, but not recommended.
18459  * <br><br>
18460  *
18461  * A server request is sent, and child nodes are loaded only when a node is expanded.
18462  * The loading node's id is passed to the server under the parameter name "node" to
18463  * enable the server to produce the correct child nodes.
18464  * <br><br>
18465  * To pass extra parameters, an event handler may be attached to the "beforeload"
18466  * event, and the parameters specified in the TreeLoader's baseParams property:
18467  * <pre><code>
18468     myTreeLoader.on("beforeload", function(treeLoader, node) {
18469         this.baseParams.category = node.attributes.category;
18470     }, this);
18471 </code></pre><
18472  * This would pass an HTTP parameter called "category" to the server containing
18473  * the value of the Node's "category" attribute.
18474  * @constructor
18475  * Creates a new Treeloader.
18476  * @param {Object} config A config object containing config properties.
18477  */
18478 Roo.tree.TreeLoader = function(config){
18479     this.baseParams = {};
18480     this.requestMethod = "POST";
18481     Roo.apply(this, config);
18482
18483     this.addEvents({
18484     
18485         /**
18486          * @event beforeload
18487          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18488          * @param {Object} This TreeLoader object.
18489          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18490          * @param {Object} callback The callback function specified in the {@link #load} call.
18491          */
18492         beforeload : true,
18493         /**
18494          * @event load
18495          * Fires when the node has been successfuly loaded.
18496          * @param {Object} This TreeLoader object.
18497          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18498          * @param {Object} response The response object containing the data from the server.
18499          */
18500         load : true,
18501         /**
18502          * @event loadexception
18503          * Fires if the network request failed.
18504          * @param {Object} This TreeLoader object.
18505          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18506          * @param {Object} response The response object containing the data from the server.
18507          */
18508         loadexception : true,
18509         /**
18510          * @event create
18511          * Fires before a node is created, enabling you to return custom Node types 
18512          * @param {Object} This TreeLoader object.
18513          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18514          */
18515         create : true
18516     });
18517
18518     Roo.tree.TreeLoader.superclass.constructor.call(this);
18519 };
18520
18521 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18522     /**
18523     * @cfg {String} dataUrl The URL from which to request a Json string which
18524     * specifies an array of node definition object representing the child nodes
18525     * to be loaded.
18526     */
18527     /**
18528     * @cfg {String} requestMethod either GET or POST
18529     * defaults to POST (due to BC)
18530     * to be loaded.
18531     */
18532     /**
18533     * @cfg {Object} baseParams (optional) An object containing properties which
18534     * specify HTTP parameters to be passed to each request for child nodes.
18535     */
18536     /**
18537     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18538     * created by this loader. If the attributes sent by the server have an attribute in this object,
18539     * they take priority.
18540     */
18541     /**
18542     * @cfg {Object} uiProviders (optional) An object containing properties which
18543     * 
18544     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
18545     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18546     * <i>uiProvider</i> attribute of a returned child node is a string rather
18547     * than a reference to a TreeNodeUI implementation, this that string value
18548     * is used as a property name in the uiProviders object. You can define the provider named
18549     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18550     */
18551     uiProviders : {},
18552
18553     /**
18554     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18555     * child nodes before loading.
18556     */
18557     clearOnLoad : true,
18558
18559     /**
18560     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18561     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18562     * Grid query { data : [ .....] }
18563     */
18564     
18565     root : false,
18566      /**
18567     * @cfg {String} queryParam (optional) 
18568     * Name of the query as it will be passed on the querystring (defaults to 'node')
18569     * eg. the request will be ?node=[id]
18570     */
18571     
18572     
18573     queryParam: false,
18574     
18575     /**
18576      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18577      * This is called automatically when a node is expanded, but may be used to reload
18578      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18579      * @param {Roo.tree.TreeNode} node
18580      * @param {Function} callback
18581      */
18582     load : function(node, callback){
18583         if(this.clearOnLoad){
18584             while(node.firstChild){
18585                 node.removeChild(node.firstChild);
18586             }
18587         }
18588         if(node.attributes.children){ // preloaded json children
18589             var cs = node.attributes.children;
18590             for(var i = 0, len = cs.length; i < len; i++){
18591                 node.appendChild(this.createNode(cs[i]));
18592             }
18593             if(typeof callback == "function"){
18594                 callback();
18595             }
18596         }else if(this.dataUrl){
18597             this.requestData(node, callback);
18598         }
18599     },
18600
18601     getParams: function(node){
18602         var buf = [], bp = this.baseParams;
18603         for(var key in bp){
18604             if(typeof bp[key] != "function"){
18605                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18606             }
18607         }
18608         var n = this.queryParam === false ? 'node' : this.queryParam;
18609         buf.push(n + "=", encodeURIComponent(node.id));
18610         return buf.join("");
18611     },
18612
18613     requestData : function(node, callback){
18614         if(this.fireEvent("beforeload", this, node, callback) !== false){
18615             this.transId = Roo.Ajax.request({
18616                 method:this.requestMethod,
18617                 url: this.dataUrl||this.url,
18618                 success: this.handleResponse,
18619                 failure: this.handleFailure,
18620                 scope: this,
18621                 argument: {callback: callback, node: node},
18622                 params: this.getParams(node)
18623             });
18624         }else{
18625             // if the load is cancelled, make sure we notify
18626             // the node that we are done
18627             if(typeof callback == "function"){
18628                 callback();
18629             }
18630         }
18631     },
18632
18633     isLoading : function(){
18634         return this.transId ? true : false;
18635     },
18636
18637     abort : function(){
18638         if(this.isLoading()){
18639             Roo.Ajax.abort(this.transId);
18640         }
18641     },
18642
18643     // private
18644     createNode : function(attr)
18645     {
18646         // apply baseAttrs, nice idea Corey!
18647         if(this.baseAttrs){
18648             Roo.applyIf(attr, this.baseAttrs);
18649         }
18650         if(this.applyLoader !== false){
18651             attr.loader = this;
18652         }
18653         // uiProvider = depreciated..
18654         
18655         if(typeof(attr.uiProvider) == 'string'){
18656            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18657                 /**  eval:var:attr */ eval(attr.uiProvider);
18658         }
18659         if(typeof(this.uiProviders['default']) != 'undefined') {
18660             attr.uiProvider = this.uiProviders['default'];
18661         }
18662         
18663         this.fireEvent('create', this, attr);
18664         
18665         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18666         return(attr.leaf ?
18667                         new Roo.tree.TreeNode(attr) :
18668                         new Roo.tree.AsyncTreeNode(attr));
18669     },
18670
18671     processResponse : function(response, node, callback)
18672     {
18673         var json = response.responseText;
18674         try {
18675             
18676             var o = Roo.decode(json);
18677             
18678             if (this.root === false && typeof(o.success) != undefined) {
18679                 this.root = 'data'; // the default behaviour for list like data..
18680                 }
18681                 
18682             if (this.root !== false &&  !o.success) {
18683                 // it's a failure condition.
18684                 var a = response.argument;
18685                 this.fireEvent("loadexception", this, a.node, response);
18686                 Roo.log("Load failed - should have a handler really");
18687                 return;
18688             }
18689             
18690             
18691             
18692             if (this.root !== false) {
18693                  o = o[this.root];
18694             }
18695             
18696             for(var i = 0, len = o.length; i < len; i++){
18697                 var n = this.createNode(o[i]);
18698                 if(n){
18699                     node.appendChild(n);
18700                 }
18701             }
18702             if(typeof callback == "function"){
18703                 callback(this, node);
18704             }
18705         }catch(e){
18706             this.handleFailure(response);
18707         }
18708     },
18709
18710     handleResponse : function(response){
18711         this.transId = false;
18712         var a = response.argument;
18713         this.processResponse(response, a.node, a.callback);
18714         this.fireEvent("load", this, a.node, response);
18715     },
18716
18717     handleFailure : function(response)
18718     {
18719         // should handle failure better..
18720         this.transId = false;
18721         var a = response.argument;
18722         this.fireEvent("loadexception", this, a.node, response);
18723         if(typeof a.callback == "function"){
18724             a.callback(this, a.node);
18725         }
18726     }
18727 });/*
18728  * Based on:
18729  * Ext JS Library 1.1.1
18730  * Copyright(c) 2006-2007, Ext JS, LLC.
18731  *
18732  * Originally Released Under LGPL - original licence link has changed is not relivant.
18733  *
18734  * Fork - LGPL
18735  * <script type="text/javascript">
18736  */
18737
18738 /**
18739 * @class Roo.tree.TreeFilter
18740 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18741 * @param {TreePanel} tree
18742 * @param {Object} config (optional)
18743  */
18744 Roo.tree.TreeFilter = function(tree, config){
18745     this.tree = tree;
18746     this.filtered = {};
18747     Roo.apply(this, config);
18748 };
18749
18750 Roo.tree.TreeFilter.prototype = {
18751     clearBlank:false,
18752     reverse:false,
18753     autoClear:false,
18754     remove:false,
18755
18756      /**
18757      * Filter the data by a specific attribute.
18758      * @param {String/RegExp} value Either string that the attribute value
18759      * should start with or a RegExp to test against the attribute
18760      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18761      * @param {TreeNode} startNode (optional) The node to start the filter at.
18762      */
18763     filter : function(value, attr, startNode){
18764         attr = attr || "text";
18765         var f;
18766         if(typeof value == "string"){
18767             var vlen = value.length;
18768             // auto clear empty filter
18769             if(vlen == 0 && this.clearBlank){
18770                 this.clear();
18771                 return;
18772             }
18773             value = value.toLowerCase();
18774             f = function(n){
18775                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18776             };
18777         }else if(value.exec){ // regex?
18778             f = function(n){
18779                 return value.test(n.attributes[attr]);
18780             };
18781         }else{
18782             throw 'Illegal filter type, must be string or regex';
18783         }
18784         this.filterBy(f, null, startNode);
18785         },
18786
18787     /**
18788      * Filter by a function. The passed function will be called with each
18789      * node in the tree (or from the startNode). If the function returns true, the node is kept
18790      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18791      * @param {Function} fn The filter function
18792      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18793      */
18794     filterBy : function(fn, scope, startNode){
18795         startNode = startNode || this.tree.root;
18796         if(this.autoClear){
18797             this.clear();
18798         }
18799         var af = this.filtered, rv = this.reverse;
18800         var f = function(n){
18801             if(n == startNode){
18802                 return true;
18803             }
18804             if(af[n.id]){
18805                 return false;
18806             }
18807             var m = fn.call(scope || n, n);
18808             if(!m || rv){
18809                 af[n.id] = n;
18810                 n.ui.hide();
18811                 return false;
18812             }
18813             return true;
18814         };
18815         startNode.cascade(f);
18816         if(this.remove){
18817            for(var id in af){
18818                if(typeof id != "function"){
18819                    var n = af[id];
18820                    if(n && n.parentNode){
18821                        n.parentNode.removeChild(n);
18822                    }
18823                }
18824            }
18825         }
18826     },
18827
18828     /**
18829      * Clears the current filter. Note: with the "remove" option
18830      * set a filter cannot be cleared.
18831      */
18832     clear : function(){
18833         var t = this.tree;
18834         var af = this.filtered;
18835         for(var id in af){
18836             if(typeof id != "function"){
18837                 var n = af[id];
18838                 if(n){
18839                     n.ui.show();
18840                 }
18841             }
18842         }
18843         this.filtered = {};
18844     }
18845 };
18846 /*
18847  * Based on:
18848  * Ext JS Library 1.1.1
18849  * Copyright(c) 2006-2007, Ext JS, LLC.
18850  *
18851  * Originally Released Under LGPL - original licence link has changed is not relivant.
18852  *
18853  * Fork - LGPL
18854  * <script type="text/javascript">
18855  */
18856  
18857
18858 /**
18859  * @class Roo.tree.TreeSorter
18860  * Provides sorting of nodes in a TreePanel
18861  * 
18862  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18863  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18864  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18865  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18866  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18867  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18868  * @constructor
18869  * @param {TreePanel} tree
18870  * @param {Object} config
18871  */
18872 Roo.tree.TreeSorter = function(tree, config){
18873     Roo.apply(this, config);
18874     tree.on("beforechildrenrendered", this.doSort, this);
18875     tree.on("append", this.updateSort, this);
18876     tree.on("insert", this.updateSort, this);
18877     
18878     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18879     var p = this.property || "text";
18880     var sortType = this.sortType;
18881     var fs = this.folderSort;
18882     var cs = this.caseSensitive === true;
18883     var leafAttr = this.leafAttr || 'leaf';
18884
18885     this.sortFn = function(n1, n2){
18886         if(fs){
18887             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18888                 return 1;
18889             }
18890             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18891                 return -1;
18892             }
18893         }
18894         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18895         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18896         if(v1 < v2){
18897                         return dsc ? +1 : -1;
18898                 }else if(v1 > v2){
18899                         return dsc ? -1 : +1;
18900         }else{
18901                 return 0;
18902         }
18903     };
18904 };
18905
18906 Roo.tree.TreeSorter.prototype = {
18907     doSort : function(node){
18908         node.sort(this.sortFn);
18909     },
18910     
18911     compareNodes : function(n1, n2){
18912         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18913     },
18914     
18915     updateSort : function(tree, node){
18916         if(node.childrenRendered){
18917             this.doSort.defer(1, this, [node]);
18918         }
18919     }
18920 };/*
18921  * Based on:
18922  * Ext JS Library 1.1.1
18923  * Copyright(c) 2006-2007, Ext JS, LLC.
18924  *
18925  * Originally Released Under LGPL - original licence link has changed is not relivant.
18926  *
18927  * Fork - LGPL
18928  * <script type="text/javascript">
18929  */
18930
18931 if(Roo.dd.DropZone){
18932     
18933 Roo.tree.TreeDropZone = function(tree, config){
18934     this.allowParentInsert = false;
18935     this.allowContainerDrop = false;
18936     this.appendOnly = false;
18937     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18938     this.tree = tree;
18939     this.lastInsertClass = "x-tree-no-status";
18940     this.dragOverData = {};
18941 };
18942
18943 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18944     ddGroup : "TreeDD",
18945     scroll:  true,
18946     
18947     expandDelay : 1000,
18948     
18949     expandNode : function(node){
18950         if(node.hasChildNodes() && !node.isExpanded()){
18951             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18952         }
18953     },
18954     
18955     queueExpand : function(node){
18956         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18957     },
18958     
18959     cancelExpand : function(){
18960         if(this.expandProcId){
18961             clearTimeout(this.expandProcId);
18962             this.expandProcId = false;
18963         }
18964     },
18965     
18966     isValidDropPoint : function(n, pt, dd, e, data){
18967         if(!n || !data){ return false; }
18968         var targetNode = n.node;
18969         var dropNode = data.node;
18970         // default drop rules
18971         if(!(targetNode && targetNode.isTarget && pt)){
18972             return false;
18973         }
18974         if(pt == "append" && targetNode.allowChildren === false){
18975             return false;
18976         }
18977         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18978             return false;
18979         }
18980         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18981             return false;
18982         }
18983         // reuse the object
18984         var overEvent = this.dragOverData;
18985         overEvent.tree = this.tree;
18986         overEvent.target = targetNode;
18987         overEvent.data = data;
18988         overEvent.point = pt;
18989         overEvent.source = dd;
18990         overEvent.rawEvent = e;
18991         overEvent.dropNode = dropNode;
18992         overEvent.cancel = false;  
18993         var result = this.tree.fireEvent("nodedragover", overEvent);
18994         return overEvent.cancel === false && result !== false;
18995     },
18996     
18997     getDropPoint : function(e, n, dd)
18998     {
18999         var tn = n.node;
19000         if(tn.isRoot){
19001             return tn.allowChildren !== false ? "append" : false; // always append for root
19002         }
19003         var dragEl = n.ddel;
19004         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
19005         var y = Roo.lib.Event.getPageY(e);
19006         //var noAppend = tn.allowChildren === false || tn.isLeaf();
19007         
19008         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
19009         var noAppend = tn.allowChildren === false;
19010         if(this.appendOnly || tn.parentNode.allowChildren === false){
19011             return noAppend ? false : "append";
19012         }
19013         var noBelow = false;
19014         if(!this.allowParentInsert){
19015             noBelow = tn.hasChildNodes() && tn.isExpanded();
19016         }
19017         var q = (b - t) / (noAppend ? 2 : 3);
19018         if(y >= t && y < (t + q)){
19019             return "above";
19020         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
19021             return "below";
19022         }else{
19023             return "append";
19024         }
19025     },
19026     
19027     onNodeEnter : function(n, dd, e, data)
19028     {
19029         this.cancelExpand();
19030     },
19031     
19032     onNodeOver : function(n, dd, e, data)
19033     {
19034        
19035         var pt = this.getDropPoint(e, n, dd);
19036         var node = n.node;
19037         
19038         // auto node expand check
19039         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
19040             this.queueExpand(node);
19041         }else if(pt != "append"){
19042             this.cancelExpand();
19043         }
19044         
19045         // set the insert point style on the target node
19046         var returnCls = this.dropNotAllowed;
19047         if(this.isValidDropPoint(n, pt, dd, e, data)){
19048            if(pt){
19049                var el = n.ddel;
19050                var cls;
19051                if(pt == "above"){
19052                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
19053                    cls = "x-tree-drag-insert-above";
19054                }else if(pt == "below"){
19055                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
19056                    cls = "x-tree-drag-insert-below";
19057                }else{
19058                    returnCls = "x-tree-drop-ok-append";
19059                    cls = "x-tree-drag-append";
19060                }
19061                if(this.lastInsertClass != cls){
19062                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
19063                    this.lastInsertClass = cls;
19064                }
19065            }
19066        }
19067        return returnCls;
19068     },
19069     
19070     onNodeOut : function(n, dd, e, data){
19071         
19072         this.cancelExpand();
19073         this.removeDropIndicators(n);
19074     },
19075     
19076     onNodeDrop : function(n, dd, e, data){
19077         var point = this.getDropPoint(e, n, dd);
19078         var targetNode = n.node;
19079         targetNode.ui.startDrop();
19080         if(!this.isValidDropPoint(n, point, dd, e, data)){
19081             targetNode.ui.endDrop();
19082             return false;
19083         }
19084         // first try to find the drop node
19085         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
19086         var dropEvent = {
19087             tree : this.tree,
19088             target: targetNode,
19089             data: data,
19090             point: point,
19091             source: dd,
19092             rawEvent: e,
19093             dropNode: dropNode,
19094             cancel: !dropNode   
19095         };
19096         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
19097         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
19098             targetNode.ui.endDrop();
19099             return false;
19100         }
19101         // allow target changing
19102         targetNode = dropEvent.target;
19103         if(point == "append" && !targetNode.isExpanded()){
19104             targetNode.expand(false, null, function(){
19105                 this.completeDrop(dropEvent);
19106             }.createDelegate(this));
19107         }else{
19108             this.completeDrop(dropEvent);
19109         }
19110         return true;
19111     },
19112     
19113     completeDrop : function(de){
19114         var ns = de.dropNode, p = de.point, t = de.target;
19115         if(!(ns instanceof Array)){
19116             ns = [ns];
19117         }
19118         var n;
19119         for(var i = 0, len = ns.length; i < len; i++){
19120             n = ns[i];
19121             if(p == "above"){
19122                 t.parentNode.insertBefore(n, t);
19123             }else if(p == "below"){
19124                 t.parentNode.insertBefore(n, t.nextSibling);
19125             }else{
19126                 t.appendChild(n);
19127             }
19128         }
19129         n.ui.focus();
19130         if(this.tree.hlDrop){
19131             n.ui.highlight();
19132         }
19133         t.ui.endDrop();
19134         this.tree.fireEvent("nodedrop", de);
19135     },
19136     
19137     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
19138         if(this.tree.hlDrop){
19139             dropNode.ui.focus();
19140             dropNode.ui.highlight();
19141         }
19142         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
19143     },
19144     
19145     getTree : function(){
19146         return this.tree;
19147     },
19148     
19149     removeDropIndicators : function(n){
19150         if(n && n.ddel){
19151             var el = n.ddel;
19152             Roo.fly(el).removeClass([
19153                     "x-tree-drag-insert-above",
19154                     "x-tree-drag-insert-below",
19155                     "x-tree-drag-append"]);
19156             this.lastInsertClass = "_noclass";
19157         }
19158     },
19159     
19160     beforeDragDrop : function(target, e, id){
19161         this.cancelExpand();
19162         return true;
19163     },
19164     
19165     afterRepair : function(data){
19166         if(data && Roo.enableFx){
19167             data.node.ui.highlight();
19168         }
19169         this.hideProxy();
19170     } 
19171     
19172 });
19173
19174 }
19175 /*
19176  * Based on:
19177  * Ext JS Library 1.1.1
19178  * Copyright(c) 2006-2007, Ext JS, LLC.
19179  *
19180  * Originally Released Under LGPL - original licence link has changed is not relivant.
19181  *
19182  * Fork - LGPL
19183  * <script type="text/javascript">
19184  */
19185  
19186
19187 if(Roo.dd.DragZone){
19188 Roo.tree.TreeDragZone = function(tree, config){
19189     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
19190     this.tree = tree;
19191 };
19192
19193 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
19194     ddGroup : "TreeDD",
19195    
19196     onBeforeDrag : function(data, e){
19197         var n = data.node;
19198         return n && n.draggable && !n.disabled;
19199     },
19200      
19201     
19202     onInitDrag : function(e){
19203         var data = this.dragData;
19204         this.tree.getSelectionModel().select(data.node);
19205         this.proxy.update("");
19206         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
19207         this.tree.fireEvent("startdrag", this.tree, data.node, e);
19208     },
19209     
19210     getRepairXY : function(e, data){
19211         return data.node.ui.getDDRepairXY();
19212     },
19213     
19214     onEndDrag : function(data, e){
19215         this.tree.fireEvent("enddrag", this.tree, data.node, e);
19216         
19217         
19218     },
19219     
19220     onValidDrop : function(dd, e, id){
19221         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
19222         this.hideProxy();
19223     },
19224     
19225     beforeInvalidDrop : function(e, id){
19226         // this scrolls the original position back into view
19227         var sm = this.tree.getSelectionModel();
19228         sm.clearSelections();
19229         sm.select(this.dragData.node);
19230     }
19231 });
19232 }/*
19233  * Based on:
19234  * Ext JS Library 1.1.1
19235  * Copyright(c) 2006-2007, Ext JS, LLC.
19236  *
19237  * Originally Released Under LGPL - original licence link has changed is not relivant.
19238  *
19239  * Fork - LGPL
19240  * <script type="text/javascript">
19241  */
19242 /**
19243  * @class Roo.tree.TreeEditor
19244  * @extends Roo.Editor
19245  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
19246  * as the editor field.
19247  * @constructor
19248  * @param {Object} config (used to be the tree panel.)
19249  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
19250  * 
19251  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
19252  * @cfg {Roo.form.TextField|Object} field The field configuration
19253  *
19254  * 
19255  */
19256 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
19257     var tree = config;
19258     var field;
19259     if (oldconfig) { // old style..
19260         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
19261     } else {
19262         // new style..
19263         tree = config.tree;
19264         config.field = config.field  || {};
19265         config.field.xtype = 'TextField';
19266         field = Roo.factory(config.field, Roo.form);
19267     }
19268     config = config || {};
19269     
19270     
19271     this.addEvents({
19272         /**
19273          * @event beforenodeedit
19274          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
19275          * false from the handler of this event.
19276          * @param {Editor} this
19277          * @param {Roo.tree.Node} node 
19278          */
19279         "beforenodeedit" : true
19280     });
19281     
19282     //Roo.log(config);
19283     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
19284
19285     this.tree = tree;
19286
19287     tree.on('beforeclick', this.beforeNodeClick, this);
19288     tree.getTreeEl().on('mousedown', this.hide, this);
19289     this.on('complete', this.updateNode, this);
19290     this.on('beforestartedit', this.fitToTree, this);
19291     this.on('startedit', this.bindScroll, this, {delay:10});
19292     this.on('specialkey', this.onSpecialKey, this);
19293 };
19294
19295 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
19296     /**
19297      * @cfg {String} alignment
19298      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
19299      */
19300     alignment: "l-l",
19301     // inherit
19302     autoSize: false,
19303     /**
19304      * @cfg {Boolean} hideEl
19305      * True to hide the bound element while the editor is displayed (defaults to false)
19306      */
19307     hideEl : false,
19308     /**
19309      * @cfg {String} cls
19310      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
19311      */
19312     cls: "x-small-editor x-tree-editor",
19313     /**
19314      * @cfg {Boolean} shim
19315      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
19316      */
19317     shim:false,
19318     // inherit
19319     shadow:"frame",
19320     /**
19321      * @cfg {Number} maxWidth
19322      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
19323      * the containing tree element's size, it will be automatically limited for you to the container width, taking
19324      * scroll and client offsets into account prior to each edit.
19325      */
19326     maxWidth: 250,
19327
19328     editDelay : 350,
19329
19330     // private
19331     fitToTree : function(ed, el){
19332         var td = this.tree.getTreeEl().dom, nd = el.dom;
19333         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
19334             td.scrollLeft = nd.offsetLeft;
19335         }
19336         var w = Math.min(
19337                 this.maxWidth,
19338                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
19339         this.setSize(w, '');
19340         
19341         return this.fireEvent('beforenodeedit', this, this.editNode);
19342         
19343     },
19344
19345     // private
19346     triggerEdit : function(node){
19347         this.completeEdit();
19348         this.editNode = node;
19349         this.startEdit(node.ui.textNode, node.text);
19350     },
19351
19352     // private
19353     bindScroll : function(){
19354         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
19355     },
19356
19357     // private
19358     beforeNodeClick : function(node, e){
19359         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19360         this.lastClick = new Date();
19361         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19362             e.stopEvent();
19363             this.triggerEdit(node);
19364             return false;
19365         }
19366         return true;
19367     },
19368
19369     // private
19370     updateNode : function(ed, value){
19371         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19372         this.editNode.setText(value);
19373     },
19374
19375     // private
19376     onHide : function(){
19377         Roo.tree.TreeEditor.superclass.onHide.call(this);
19378         if(this.editNode){
19379             this.editNode.ui.focus();
19380         }
19381     },
19382
19383     // private
19384     onSpecialKey : function(field, e){
19385         var k = e.getKey();
19386         if(k == e.ESC){
19387             e.stopEvent();
19388             this.cancelEdit();
19389         }else if(k == e.ENTER && !e.hasModifier()){
19390             e.stopEvent();
19391             this.completeEdit();
19392         }
19393     }
19394 });//<Script type="text/javascript">
19395 /*
19396  * Based on:
19397  * Ext JS Library 1.1.1
19398  * Copyright(c) 2006-2007, Ext JS, LLC.
19399  *
19400  * Originally Released Under LGPL - original licence link has changed is not relivant.
19401  *
19402  * Fork - LGPL
19403  * <script type="text/javascript">
19404  */
19405  
19406 /**
19407  * Not documented??? - probably should be...
19408  */
19409
19410 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19411     //focus: Roo.emptyFn, // prevent odd scrolling behavior
19412     
19413     renderElements : function(n, a, targetNode, bulkRender){
19414         //consel.log("renderElements?");
19415         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19416
19417         var t = n.getOwnerTree();
19418         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19419         
19420         var cols = t.columns;
19421         var bw = t.borderWidth;
19422         var c = cols[0];
19423         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19424          var cb = typeof a.checked == "boolean";
19425         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19426         var colcls = 'x-t-' + tid + '-c0';
19427         var buf = [
19428             '<li class="x-tree-node">',
19429             
19430                 
19431                 '<div class="x-tree-node-el ', a.cls,'">',
19432                     // extran...
19433                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19434                 
19435                 
19436                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19437                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
19438                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19439                            (a.icon ? ' x-tree-node-inline-icon' : ''),
19440                            (a.iconCls ? ' '+a.iconCls : ''),
19441                            '" unselectable="on" />',
19442                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
19443                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
19444                              
19445                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19446                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19447                             '<span unselectable="on" qtip="' + tx + '">',
19448                              tx,
19449                              '</span></a>' ,
19450                     '</div>',
19451                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19452                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19453                  ];
19454         for(var i = 1, len = cols.length; i < len; i++){
19455             c = cols[i];
19456             colcls = 'x-t-' + tid + '-c' +i;
19457             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19458             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19459                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19460                       "</div>");
19461          }
19462          
19463          buf.push(
19464             '</a>',
19465             '<div class="x-clear"></div></div>',
19466             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19467             "</li>");
19468         
19469         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19470             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19471                                 n.nextSibling.ui.getEl(), buf.join(""));
19472         }else{
19473             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19474         }
19475         var el = this.wrap.firstChild;
19476         this.elRow = el;
19477         this.elNode = el.firstChild;
19478         this.ranchor = el.childNodes[1];
19479         this.ctNode = this.wrap.childNodes[1];
19480         var cs = el.firstChild.childNodes;
19481         this.indentNode = cs[0];
19482         this.ecNode = cs[1];
19483         this.iconNode = cs[2];
19484         var index = 3;
19485         if(cb){
19486             this.checkbox = cs[3];
19487             index++;
19488         }
19489         this.anchor = cs[index];
19490         
19491         this.textNode = cs[index].firstChild;
19492         
19493         //el.on("click", this.onClick, this);
19494         //el.on("dblclick", this.onDblClick, this);
19495         
19496         
19497        // console.log(this);
19498     },
19499     initEvents : function(){
19500         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19501         
19502             
19503         var a = this.ranchor;
19504
19505         var el = Roo.get(a);
19506
19507         if(Roo.isOpera){ // opera render bug ignores the CSS
19508             el.setStyle("text-decoration", "none");
19509         }
19510
19511         el.on("click", this.onClick, this);
19512         el.on("dblclick", this.onDblClick, this);
19513         el.on("contextmenu", this.onContextMenu, this);
19514         
19515     },
19516     
19517     /*onSelectedChange : function(state){
19518         if(state){
19519             this.focus();
19520             this.addClass("x-tree-selected");
19521         }else{
19522             //this.blur();
19523             this.removeClass("x-tree-selected");
19524         }
19525     },*/
19526     addClass : function(cls){
19527         if(this.elRow){
19528             Roo.fly(this.elRow).addClass(cls);
19529         }
19530         
19531     },
19532     
19533     
19534     removeClass : function(cls){
19535         if(this.elRow){
19536             Roo.fly(this.elRow).removeClass(cls);
19537         }
19538     }
19539
19540     
19541     
19542 });//<Script type="text/javascript">
19543
19544 /*
19545  * Based on:
19546  * Ext JS Library 1.1.1
19547  * Copyright(c) 2006-2007, Ext JS, LLC.
19548  *
19549  * Originally Released Under LGPL - original licence link has changed is not relivant.
19550  *
19551  * Fork - LGPL
19552  * <script type="text/javascript">
19553  */
19554  
19555
19556 /**
19557  * @class Roo.tree.ColumnTree
19558  * @extends Roo.data.TreePanel
19559  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19560  * @cfg {int} borderWidth  compined right/left border allowance
19561  * @constructor
19562  * @param {String/HTMLElement/Element} el The container element
19563  * @param {Object} config
19564  */
19565 Roo.tree.ColumnTree =  function(el, config)
19566 {
19567    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19568    this.addEvents({
19569         /**
19570         * @event resize
19571         * Fire this event on a container when it resizes
19572         * @param {int} w Width
19573         * @param {int} h Height
19574         */
19575        "resize" : true
19576     });
19577     this.on('resize', this.onResize, this);
19578 };
19579
19580 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19581     //lines:false,
19582     
19583     
19584     borderWidth: Roo.isBorderBox ? 0 : 2, 
19585     headEls : false,
19586     
19587     render : function(){
19588         // add the header.....
19589        
19590         Roo.tree.ColumnTree.superclass.render.apply(this);
19591         
19592         this.el.addClass('x-column-tree');
19593         
19594         this.headers = this.el.createChild(
19595             {cls:'x-tree-headers'},this.innerCt.dom);
19596    
19597         var cols = this.columns, c;
19598         var totalWidth = 0;
19599         this.headEls = [];
19600         var  len = cols.length;
19601         for(var i = 0; i < len; i++){
19602              c = cols[i];
19603              totalWidth += c.width;
19604             this.headEls.push(this.headers.createChild({
19605                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19606                  cn: {
19607                      cls:'x-tree-hd-text',
19608                      html: c.header
19609                  },
19610                  style:'width:'+(c.width-this.borderWidth)+'px;'
19611              }));
19612         }
19613         this.headers.createChild({cls:'x-clear'});
19614         // prevent floats from wrapping when clipped
19615         this.headers.setWidth(totalWidth);
19616         //this.innerCt.setWidth(totalWidth);
19617         this.innerCt.setStyle({ overflow: 'auto' });
19618         this.onResize(this.width, this.height);
19619              
19620         
19621     },
19622     onResize : function(w,h)
19623     {
19624         this.height = h;
19625         this.width = w;
19626         // resize cols..
19627         this.innerCt.setWidth(this.width);
19628         this.innerCt.setHeight(this.height-20);
19629         
19630         // headers...
19631         var cols = this.columns, c;
19632         var totalWidth = 0;
19633         var expEl = false;
19634         var len = cols.length;
19635         for(var i = 0; i < len; i++){
19636             c = cols[i];
19637             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19638                 // it's the expander..
19639                 expEl  = this.headEls[i];
19640                 continue;
19641             }
19642             totalWidth += c.width;
19643             
19644         }
19645         if (expEl) {
19646             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19647         }
19648         this.headers.setWidth(w-20);
19649
19650         
19651         
19652         
19653     }
19654 });
19655 /*
19656  * Based on:
19657  * Ext JS Library 1.1.1
19658  * Copyright(c) 2006-2007, Ext JS, LLC.
19659  *
19660  * Originally Released Under LGPL - original licence link has changed is not relivant.
19661  *
19662  * Fork - LGPL
19663  * <script type="text/javascript">
19664  */
19665  
19666 /**
19667  * @class Roo.menu.Menu
19668  * @extends Roo.util.Observable
19669  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19670  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19671  * @constructor
19672  * Creates a new Menu
19673  * @param {Object} config Configuration options
19674  */
19675 Roo.menu.Menu = function(config){
19676     Roo.apply(this, config);
19677     this.id = this.id || Roo.id();
19678     this.addEvents({
19679         /**
19680          * @event beforeshow
19681          * Fires before this menu is displayed
19682          * @param {Roo.menu.Menu} this
19683          */
19684         beforeshow : true,
19685         /**
19686          * @event beforehide
19687          * Fires before this menu is hidden
19688          * @param {Roo.menu.Menu} this
19689          */
19690         beforehide : true,
19691         /**
19692          * @event show
19693          * Fires after this menu is displayed
19694          * @param {Roo.menu.Menu} this
19695          */
19696         show : true,
19697         /**
19698          * @event hide
19699          * Fires after this menu is hidden
19700          * @param {Roo.menu.Menu} this
19701          */
19702         hide : true,
19703         /**
19704          * @event click
19705          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19706          * @param {Roo.menu.Menu} this
19707          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19708          * @param {Roo.EventObject} e
19709          */
19710         click : true,
19711         /**
19712          * @event mouseover
19713          * Fires when the mouse is hovering over this menu
19714          * @param {Roo.menu.Menu} this
19715          * @param {Roo.EventObject} e
19716          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19717          */
19718         mouseover : true,
19719         /**
19720          * @event mouseout
19721          * Fires when the mouse exits this menu
19722          * @param {Roo.menu.Menu} this
19723          * @param {Roo.EventObject} e
19724          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19725          */
19726         mouseout : true,
19727         /**
19728          * @event itemclick
19729          * Fires when a menu item contained in this menu is clicked
19730          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19731          * @param {Roo.EventObject} e
19732          */
19733         itemclick: true
19734     });
19735     if (this.registerMenu) {
19736         Roo.menu.MenuMgr.register(this);
19737     }
19738     
19739     var mis = this.items;
19740     this.items = new Roo.util.MixedCollection();
19741     if(mis){
19742         this.add.apply(this, mis);
19743     }
19744 };
19745
19746 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19747     /**
19748      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19749      */
19750     minWidth : 120,
19751     /**
19752      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19753      * for bottom-right shadow (defaults to "sides")
19754      */
19755     shadow : "sides",
19756     /**
19757      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19758      * this menu (defaults to "tl-tr?")
19759      */
19760     subMenuAlign : "tl-tr?",
19761     /**
19762      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19763      * relative to its element of origin (defaults to "tl-bl?")
19764      */
19765     defaultAlign : "tl-bl?",
19766     /**
19767      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19768      */
19769     allowOtherMenus : false,
19770     /**
19771      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19772      */
19773     registerMenu : true,
19774
19775     hidden:true,
19776
19777     // private
19778     render : function(){
19779         if(this.el){
19780             return;
19781         }
19782         var el = this.el = new Roo.Layer({
19783             cls: "x-menu",
19784             shadow:this.shadow,
19785             constrain: false,
19786             parentEl: this.parentEl || document.body,
19787             zindex:15000
19788         });
19789
19790         this.keyNav = new Roo.menu.MenuNav(this);
19791
19792         if(this.plain){
19793             el.addClass("x-menu-plain");
19794         }
19795         if(this.cls){
19796             el.addClass(this.cls);
19797         }
19798         // generic focus element
19799         this.focusEl = el.createChild({
19800             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19801         });
19802         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19803         ul.on("click", this.onClick, this);
19804         ul.on("mouseover", this.onMouseOver, this);
19805         ul.on("mouseout", this.onMouseOut, this);
19806         this.items.each(function(item){
19807             var li = document.createElement("li");
19808             li.className = "x-menu-list-item";
19809             ul.dom.appendChild(li);
19810             item.render(li, this);
19811         }, this);
19812         this.ul = ul;
19813         this.autoWidth();
19814     },
19815
19816     // private
19817     autoWidth : function(){
19818         var el = this.el, ul = this.ul;
19819         if(!el){
19820             return;
19821         }
19822         var w = this.width;
19823         if(w){
19824             el.setWidth(w);
19825         }else if(Roo.isIE){
19826             el.setWidth(this.minWidth);
19827             var t = el.dom.offsetWidth; // force recalc
19828             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19829         }
19830     },
19831
19832     // private
19833     delayAutoWidth : function(){
19834         if(this.rendered){
19835             if(!this.awTask){
19836                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19837             }
19838             this.awTask.delay(20);
19839         }
19840     },
19841
19842     // private
19843     findTargetItem : function(e){
19844         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19845         if(t && t.menuItemId){
19846             return this.items.get(t.menuItemId);
19847         }
19848     },
19849
19850     // private
19851     onClick : function(e){
19852         var t;
19853         if(t = this.findTargetItem(e)){
19854             t.onClick(e);
19855             this.fireEvent("click", this, t, e);
19856         }
19857     },
19858
19859     // private
19860     setActiveItem : function(item, autoExpand){
19861         if(item != this.activeItem){
19862             if(this.activeItem){
19863                 this.activeItem.deactivate();
19864             }
19865             this.activeItem = item;
19866             item.activate(autoExpand);
19867         }else if(autoExpand){
19868             item.expandMenu();
19869         }
19870     },
19871
19872     // private
19873     tryActivate : function(start, step){
19874         var items = this.items;
19875         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19876             var item = items.get(i);
19877             if(!item.disabled && item.canActivate){
19878                 this.setActiveItem(item, false);
19879                 return item;
19880             }
19881         }
19882         return false;
19883     },
19884
19885     // private
19886     onMouseOver : function(e){
19887         var t;
19888         if(t = this.findTargetItem(e)){
19889             if(t.canActivate && !t.disabled){
19890                 this.setActiveItem(t, true);
19891             }
19892         }
19893         this.fireEvent("mouseover", this, e, t);
19894     },
19895
19896     // private
19897     onMouseOut : function(e){
19898         var t;
19899         if(t = this.findTargetItem(e)){
19900             if(t == this.activeItem && t.shouldDeactivate(e)){
19901                 this.activeItem.deactivate();
19902                 delete this.activeItem;
19903             }
19904         }
19905         this.fireEvent("mouseout", this, e, t);
19906     },
19907
19908     /**
19909      * Read-only.  Returns true if the menu is currently displayed, else false.
19910      * @type Boolean
19911      */
19912     isVisible : function(){
19913         return this.el && !this.hidden;
19914     },
19915
19916     /**
19917      * Displays this menu relative to another element
19918      * @param {String/HTMLElement/Roo.Element} element The element to align to
19919      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19920      * the element (defaults to this.defaultAlign)
19921      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19922      */
19923     show : function(el, pos, parentMenu){
19924         this.parentMenu = parentMenu;
19925         if(!this.el){
19926             this.render();
19927         }
19928         this.fireEvent("beforeshow", this);
19929         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19930     },
19931
19932     /**
19933      * Displays this menu at a specific xy position
19934      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19935      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19936      */
19937     showAt : function(xy, parentMenu, /* private: */_e){
19938         this.parentMenu = parentMenu;
19939         if(!this.el){
19940             this.render();
19941         }
19942         if(_e !== false){
19943             this.fireEvent("beforeshow", this);
19944             xy = this.el.adjustForConstraints(xy);
19945         }
19946         this.el.setXY(xy);
19947         this.el.show();
19948         this.hidden = false;
19949         this.focus();
19950         this.fireEvent("show", this);
19951     },
19952
19953     focus : function(){
19954         if(!this.hidden){
19955             this.doFocus.defer(50, this);
19956         }
19957     },
19958
19959     doFocus : function(){
19960         if(!this.hidden){
19961             this.focusEl.focus();
19962         }
19963     },
19964
19965     /**
19966      * Hides this menu and optionally all parent menus
19967      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19968      */
19969     hide : function(deep){
19970         if(this.el && this.isVisible()){
19971             this.fireEvent("beforehide", this);
19972             if(this.activeItem){
19973                 this.activeItem.deactivate();
19974                 this.activeItem = null;
19975             }
19976             this.el.hide();
19977             this.hidden = true;
19978             this.fireEvent("hide", this);
19979         }
19980         if(deep === true && this.parentMenu){
19981             this.parentMenu.hide(true);
19982         }
19983     },
19984
19985     /**
19986      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19987      * Any of the following are valid:
19988      * <ul>
19989      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19990      * <li>An HTMLElement object which will be converted to a menu item</li>
19991      * <li>A menu item config object that will be created as a new menu item</li>
19992      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19993      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19994      * </ul>
19995      * Usage:
19996      * <pre><code>
19997 // Create the menu
19998 var menu = new Roo.menu.Menu();
19999
20000 // Create a menu item to add by reference
20001 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
20002
20003 // Add a bunch of items at once using different methods.
20004 // Only the last item added will be returned.
20005 var item = menu.add(
20006     menuItem,                // add existing item by ref
20007     'Dynamic Item',          // new TextItem
20008     '-',                     // new separator
20009     { text: 'Config Item' }  // new item by config
20010 );
20011 </code></pre>
20012      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
20013      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
20014      */
20015     add : function(){
20016         var a = arguments, l = a.length, item;
20017         for(var i = 0; i < l; i++){
20018             var el = a[i];
20019             if ((typeof(el) == "object") && el.xtype && el.xns) {
20020                 el = Roo.factory(el, Roo.menu);
20021             }
20022             
20023             if(el.render){ // some kind of Item
20024                 item = this.addItem(el);
20025             }else if(typeof el == "string"){ // string
20026                 if(el == "separator" || el == "-"){
20027                     item = this.addSeparator();
20028                 }else{
20029                     item = this.addText(el);
20030                 }
20031             }else if(el.tagName || el.el){ // element
20032                 item = this.addElement(el);
20033             }else if(typeof el == "object"){ // must be menu item config?
20034                 item = this.addMenuItem(el);
20035             }
20036         }
20037         return item;
20038     },
20039
20040     /**
20041      * Returns this menu's underlying {@link Roo.Element} object
20042      * @return {Roo.Element} The element
20043      */
20044     getEl : function(){
20045         if(!this.el){
20046             this.render();
20047         }
20048         return this.el;
20049     },
20050
20051     /**
20052      * Adds a separator bar to the menu
20053      * @return {Roo.menu.Item} The menu item that was added
20054      */
20055     addSeparator : function(){
20056         return this.addItem(new Roo.menu.Separator());
20057     },
20058
20059     /**
20060      * Adds an {@link Roo.Element} object to the menu
20061      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
20062      * @return {Roo.menu.Item} The menu item that was added
20063      */
20064     addElement : function(el){
20065         return this.addItem(new Roo.menu.BaseItem(el));
20066     },
20067
20068     /**
20069      * Adds an existing object based on {@link Roo.menu.Item} to the menu
20070      * @param {Roo.menu.Item} item The menu item to add
20071      * @return {Roo.menu.Item} The menu item that was added
20072      */
20073     addItem : function(item){
20074         this.items.add(item);
20075         if(this.ul){
20076             var li = document.createElement("li");
20077             li.className = "x-menu-list-item";
20078             this.ul.dom.appendChild(li);
20079             item.render(li, this);
20080             this.delayAutoWidth();
20081         }
20082         return item;
20083     },
20084
20085     /**
20086      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
20087      * @param {Object} config A MenuItem config object
20088      * @return {Roo.menu.Item} The menu item that was added
20089      */
20090     addMenuItem : function(config){
20091         if(!(config instanceof Roo.menu.Item)){
20092             if(typeof config.checked == "boolean"){ // must be check menu item config?
20093                 config = new Roo.menu.CheckItem(config);
20094             }else{
20095                 config = new Roo.menu.Item(config);
20096             }
20097         }
20098         return this.addItem(config);
20099     },
20100
20101     /**
20102      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
20103      * @param {String} text The text to display in the menu item
20104      * @return {Roo.menu.Item} The menu item that was added
20105      */
20106     addText : function(text){
20107         return this.addItem(new Roo.menu.TextItem({ text : text }));
20108     },
20109
20110     /**
20111      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
20112      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
20113      * @param {Roo.menu.Item} item The menu item to add
20114      * @return {Roo.menu.Item} The menu item that was added
20115      */
20116     insert : function(index, item){
20117         this.items.insert(index, item);
20118         if(this.ul){
20119             var li = document.createElement("li");
20120             li.className = "x-menu-list-item";
20121             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
20122             item.render(li, this);
20123             this.delayAutoWidth();
20124         }
20125         return item;
20126     },
20127
20128     /**
20129      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
20130      * @param {Roo.menu.Item} item The menu item to remove
20131      */
20132     remove : function(item){
20133         this.items.removeKey(item.id);
20134         item.destroy();
20135     },
20136
20137     /**
20138      * Removes and destroys all items in the menu
20139      */
20140     removeAll : function(){
20141         var f;
20142         while(f = this.items.first()){
20143             this.remove(f);
20144         }
20145     }
20146 });
20147
20148 // MenuNav is a private utility class used internally by the Menu
20149 Roo.menu.MenuNav = function(menu){
20150     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
20151     this.scope = this.menu = menu;
20152 };
20153
20154 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
20155     doRelay : function(e, h){
20156         var k = e.getKey();
20157         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
20158             this.menu.tryActivate(0, 1);
20159             return false;
20160         }
20161         return h.call(this.scope || this, e, this.menu);
20162     },
20163
20164     up : function(e, m){
20165         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
20166             m.tryActivate(m.items.length-1, -1);
20167         }
20168     },
20169
20170     down : function(e, m){
20171         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
20172             m.tryActivate(0, 1);
20173         }
20174     },
20175
20176     right : function(e, m){
20177         if(m.activeItem){
20178             m.activeItem.expandMenu(true);
20179         }
20180     },
20181
20182     left : function(e, m){
20183         m.hide();
20184         if(m.parentMenu && m.parentMenu.activeItem){
20185             m.parentMenu.activeItem.activate();
20186         }
20187     },
20188
20189     enter : function(e, m){
20190         if(m.activeItem){
20191             e.stopPropagation();
20192             m.activeItem.onClick(e);
20193             m.fireEvent("click", this, m.activeItem);
20194             return true;
20195         }
20196     }
20197 });/*
20198  * Based on:
20199  * Ext JS Library 1.1.1
20200  * Copyright(c) 2006-2007, Ext JS, LLC.
20201  *
20202  * Originally Released Under LGPL - original licence link has changed is not relivant.
20203  *
20204  * Fork - LGPL
20205  * <script type="text/javascript">
20206  */
20207  
20208 /**
20209  * @class Roo.menu.MenuMgr
20210  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
20211  * @singleton
20212  */
20213 Roo.menu.MenuMgr = function(){
20214    var menus, active, groups = {}, attached = false, lastShow = new Date();
20215
20216    // private - called when first menu is created
20217    function init(){
20218        menus = {};
20219        active = new Roo.util.MixedCollection();
20220        Roo.get(document).addKeyListener(27, function(){
20221            if(active.length > 0){
20222                hideAll();
20223            }
20224        });
20225    }
20226
20227    // private
20228    function hideAll(){
20229        if(active && active.length > 0){
20230            var c = active.clone();
20231            c.each(function(m){
20232                m.hide();
20233            });
20234        }
20235    }
20236
20237    // private
20238    function onHide(m){
20239        active.remove(m);
20240        if(active.length < 1){
20241            Roo.get(document).un("mousedown", onMouseDown);
20242            attached = false;
20243        }
20244    }
20245
20246    // private
20247    function onShow(m){
20248        var last = active.last();
20249        lastShow = new Date();
20250        active.add(m);
20251        if(!attached){
20252            Roo.get(document).on("mousedown", onMouseDown);
20253            attached = true;
20254        }
20255        if(m.parentMenu){
20256           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
20257           m.parentMenu.activeChild = m;
20258        }else if(last && last.isVisible()){
20259           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
20260        }
20261    }
20262
20263    // private
20264    function onBeforeHide(m){
20265        if(m.activeChild){
20266            m.activeChild.hide();
20267        }
20268        if(m.autoHideTimer){
20269            clearTimeout(m.autoHideTimer);
20270            delete m.autoHideTimer;
20271        }
20272    }
20273
20274    // private
20275    function onBeforeShow(m){
20276        var pm = m.parentMenu;
20277        if(!pm && !m.allowOtherMenus){
20278            hideAll();
20279        }else if(pm && pm.activeChild && active != m){
20280            pm.activeChild.hide();
20281        }
20282    }
20283
20284    // private
20285    function onMouseDown(e){
20286        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
20287            hideAll();
20288        }
20289    }
20290
20291    // private
20292    function onBeforeCheck(mi, state){
20293        if(state){
20294            var g = groups[mi.group];
20295            for(var i = 0, l = g.length; i < l; i++){
20296                if(g[i] != mi){
20297                    g[i].setChecked(false);
20298                }
20299            }
20300        }
20301    }
20302
20303    return {
20304
20305        /**
20306         * Hides all menus that are currently visible
20307         */
20308        hideAll : function(){
20309             hideAll();  
20310        },
20311
20312        // private
20313        register : function(menu){
20314            if(!menus){
20315                init();
20316            }
20317            menus[menu.id] = menu;
20318            menu.on("beforehide", onBeforeHide);
20319            menu.on("hide", onHide);
20320            menu.on("beforeshow", onBeforeShow);
20321            menu.on("show", onShow);
20322            var g = menu.group;
20323            if(g && menu.events["checkchange"]){
20324                if(!groups[g]){
20325                    groups[g] = [];
20326                }
20327                groups[g].push(menu);
20328                menu.on("checkchange", onCheck);
20329            }
20330        },
20331
20332         /**
20333          * Returns a {@link Roo.menu.Menu} object
20334          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
20335          * be used to generate and return a new Menu instance.
20336          */
20337        get : function(menu){
20338            if(typeof menu == "string"){ // menu id
20339                return menus[menu];
20340            }else if(menu.events){  // menu instance
20341                return menu;
20342            }else if(typeof menu.length == 'number'){ // array of menu items?
20343                return new Roo.menu.Menu({items:menu});
20344            }else{ // otherwise, must be a config
20345                return new Roo.menu.Menu(menu);
20346            }
20347        },
20348
20349        // private
20350        unregister : function(menu){
20351            delete menus[menu.id];
20352            menu.un("beforehide", onBeforeHide);
20353            menu.un("hide", onHide);
20354            menu.un("beforeshow", onBeforeShow);
20355            menu.un("show", onShow);
20356            var g = menu.group;
20357            if(g && menu.events["checkchange"]){
20358                groups[g].remove(menu);
20359                menu.un("checkchange", onCheck);
20360            }
20361        },
20362
20363        // private
20364        registerCheckable : function(menuItem){
20365            var g = menuItem.group;
20366            if(g){
20367                if(!groups[g]){
20368                    groups[g] = [];
20369                }
20370                groups[g].push(menuItem);
20371                menuItem.on("beforecheckchange", onBeforeCheck);
20372            }
20373        },
20374
20375        // private
20376        unregisterCheckable : function(menuItem){
20377            var g = menuItem.group;
20378            if(g){
20379                groups[g].remove(menuItem);
20380                menuItem.un("beforecheckchange", onBeforeCheck);
20381            }
20382        }
20383    };
20384 }();/*
20385  * Based on:
20386  * Ext JS Library 1.1.1
20387  * Copyright(c) 2006-2007, Ext JS, LLC.
20388  *
20389  * Originally Released Under LGPL - original licence link has changed is not relivant.
20390  *
20391  * Fork - LGPL
20392  * <script type="text/javascript">
20393  */
20394  
20395
20396 /**
20397  * @class Roo.menu.BaseItem
20398  * @extends Roo.Component
20399  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
20400  * management and base configuration options shared by all menu components.
20401  * @constructor
20402  * Creates a new BaseItem
20403  * @param {Object} config Configuration options
20404  */
20405 Roo.menu.BaseItem = function(config){
20406     Roo.menu.BaseItem.superclass.constructor.call(this, config);
20407
20408     this.addEvents({
20409         /**
20410          * @event click
20411          * Fires when this item is clicked
20412          * @param {Roo.menu.BaseItem} this
20413          * @param {Roo.EventObject} e
20414          */
20415         click: true,
20416         /**
20417          * @event activate
20418          * Fires when this item is activated
20419          * @param {Roo.menu.BaseItem} this
20420          */
20421         activate : true,
20422         /**
20423          * @event deactivate
20424          * Fires when this item is deactivated
20425          * @param {Roo.menu.BaseItem} this
20426          */
20427         deactivate : true
20428     });
20429
20430     if(this.handler){
20431         this.on("click", this.handler, this.scope, true);
20432     }
20433 };
20434
20435 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20436     /**
20437      * @cfg {Function} handler
20438      * A function that will handle the click event of this menu item (defaults to undefined)
20439      */
20440     /**
20441      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20442      */
20443     canActivate : false,
20444     /**
20445      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20446      */
20447     activeClass : "x-menu-item-active",
20448     /**
20449      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20450      */
20451     hideOnClick : true,
20452     /**
20453      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20454      */
20455     hideDelay : 100,
20456
20457     // private
20458     ctype: "Roo.menu.BaseItem",
20459
20460     // private
20461     actionMode : "container",
20462
20463     // private
20464     render : function(container, parentMenu){
20465         this.parentMenu = parentMenu;
20466         Roo.menu.BaseItem.superclass.render.call(this, container);
20467         this.container.menuItemId = this.id;
20468     },
20469
20470     // private
20471     onRender : function(container, position){
20472         this.el = Roo.get(this.el);
20473         container.dom.appendChild(this.el.dom);
20474     },
20475
20476     // private
20477     onClick : function(e){
20478         if(!this.disabled && this.fireEvent("click", this, e) !== false
20479                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20480             this.handleClick(e);
20481         }else{
20482             e.stopEvent();
20483         }
20484     },
20485
20486     // private
20487     activate : function(){
20488         if(this.disabled){
20489             return false;
20490         }
20491         var li = this.container;
20492         li.addClass(this.activeClass);
20493         this.region = li.getRegion().adjust(2, 2, -2, -2);
20494         this.fireEvent("activate", this);
20495         return true;
20496     },
20497
20498     // private
20499     deactivate : function(){
20500         this.container.removeClass(this.activeClass);
20501         this.fireEvent("deactivate", this);
20502     },
20503
20504     // private
20505     shouldDeactivate : function(e){
20506         return !this.region || !this.region.contains(e.getPoint());
20507     },
20508
20509     // private
20510     handleClick : function(e){
20511         if(this.hideOnClick){
20512             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20513         }
20514     },
20515
20516     // private
20517     expandMenu : function(autoActivate){
20518         // do nothing
20519     },
20520
20521     // private
20522     hideMenu : function(){
20523         // do nothing
20524     }
20525 });/*
20526  * Based on:
20527  * Ext JS Library 1.1.1
20528  * Copyright(c) 2006-2007, Ext JS, LLC.
20529  *
20530  * Originally Released Under LGPL - original licence link has changed is not relivant.
20531  *
20532  * Fork - LGPL
20533  * <script type="text/javascript">
20534  */
20535  
20536 /**
20537  * @class Roo.menu.Adapter
20538  * @extends Roo.menu.BaseItem
20539  * A base utility class that adapts a non-menu component so that it can be wrapped by a menu item and added to a menu.
20540  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20541  * @constructor
20542  * Creates a new Adapter
20543  * @param {Object} config Configuration options
20544  */
20545 Roo.menu.Adapter = function(component, config){
20546     Roo.menu.Adapter.superclass.constructor.call(this, config);
20547     this.component = component;
20548 };
20549 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20550     // private
20551     canActivate : true,
20552
20553     // private
20554     onRender : function(container, position){
20555         this.component.render(container);
20556         this.el = this.component.getEl();
20557     },
20558
20559     // private
20560     activate : function(){
20561         if(this.disabled){
20562             return false;
20563         }
20564         this.component.focus();
20565         this.fireEvent("activate", this);
20566         return true;
20567     },
20568
20569     // private
20570     deactivate : function(){
20571         this.fireEvent("deactivate", this);
20572     },
20573
20574     // private
20575     disable : function(){
20576         this.component.disable();
20577         Roo.menu.Adapter.superclass.disable.call(this);
20578     },
20579
20580     // private
20581     enable : function(){
20582         this.component.enable();
20583         Roo.menu.Adapter.superclass.enable.call(this);
20584     }
20585 });/*
20586  * Based on:
20587  * Ext JS Library 1.1.1
20588  * Copyright(c) 2006-2007, Ext JS, LLC.
20589  *
20590  * Originally Released Under LGPL - original licence link has changed is not relivant.
20591  *
20592  * Fork - LGPL
20593  * <script type="text/javascript">
20594  */
20595
20596 /**
20597  * @class Roo.menu.TextItem
20598  * @extends Roo.menu.BaseItem
20599  * Adds a static text string to a menu, usually used as either a heading or group separator.
20600  * Note: old style constructor with text is still supported.
20601  * 
20602  * @constructor
20603  * Creates a new TextItem
20604  * @param {Object} cfg Configuration
20605  */
20606 Roo.menu.TextItem = function(cfg){
20607     if (typeof(cfg) == 'string') {
20608         this.text = cfg;
20609     } else {
20610         Roo.apply(this,cfg);
20611     }
20612     
20613     Roo.menu.TextItem.superclass.constructor.call(this);
20614 };
20615
20616 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20617     /**
20618      * @cfg {Boolean} text Text to show on item.
20619      */
20620     text : '',
20621     
20622     /**
20623      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20624      */
20625     hideOnClick : false,
20626     /**
20627      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20628      */
20629     itemCls : "x-menu-text",
20630
20631     // private
20632     onRender : function(){
20633         var s = document.createElement("span");
20634         s.className = this.itemCls;
20635         s.innerHTML = this.text;
20636         this.el = s;
20637         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20638     }
20639 });/*
20640  * Based on:
20641  * Ext JS Library 1.1.1
20642  * Copyright(c) 2006-2007, Ext JS, LLC.
20643  *
20644  * Originally Released Under LGPL - original licence link has changed is not relivant.
20645  *
20646  * Fork - LGPL
20647  * <script type="text/javascript">
20648  */
20649
20650 /**
20651  * @class Roo.menu.Separator
20652  * @extends Roo.menu.BaseItem
20653  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20654  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20655  * @constructor
20656  * @param {Object} config Configuration options
20657  */
20658 Roo.menu.Separator = function(config){
20659     Roo.menu.Separator.superclass.constructor.call(this, config);
20660 };
20661
20662 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20663     /**
20664      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20665      */
20666     itemCls : "x-menu-sep",
20667     /**
20668      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20669      */
20670     hideOnClick : false,
20671
20672     // private
20673     onRender : function(li){
20674         var s = document.createElement("span");
20675         s.className = this.itemCls;
20676         s.innerHTML = "&#160;";
20677         this.el = s;
20678         li.addClass("x-menu-sep-li");
20679         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20680     }
20681 });/*
20682  * Based on:
20683  * Ext JS Library 1.1.1
20684  * Copyright(c) 2006-2007, Ext JS, LLC.
20685  *
20686  * Originally Released Under LGPL - original licence link has changed is not relivant.
20687  *
20688  * Fork - LGPL
20689  * <script type="text/javascript">
20690  */
20691 /**
20692  * @class Roo.menu.Item
20693  * @extends Roo.menu.BaseItem
20694  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20695  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20696  * activation and click handling.
20697  * @constructor
20698  * Creates a new Item
20699  * @param {Object} config Configuration options
20700  */
20701 Roo.menu.Item = function(config){
20702     Roo.menu.Item.superclass.constructor.call(this, config);
20703     if(this.menu){
20704         this.menu = Roo.menu.MenuMgr.get(this.menu);
20705     }
20706 };
20707 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20708     
20709     /**
20710      * @cfg {String} text
20711      * The text to show on the menu item.
20712      */
20713     text: '',
20714      /**
20715      * @cfg {String} HTML to render in menu
20716      * The text to show on the menu item (HTML version).
20717      */
20718     html: '',
20719     /**
20720      * @cfg {String} icon
20721      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20722      */
20723     icon: undefined,
20724     /**
20725      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20726      */
20727     itemCls : "x-menu-item",
20728     /**
20729      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20730      */
20731     canActivate : true,
20732     /**
20733      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20734      */
20735     showDelay: 200,
20736     // doc'd in BaseItem
20737     hideDelay: 200,
20738
20739     // private
20740     ctype: "Roo.menu.Item",
20741     
20742     // private
20743     onRender : function(container, position){
20744         var el = document.createElement("a");
20745         el.hideFocus = true;
20746         el.unselectable = "on";
20747         el.href = this.href || "#";
20748         if(this.hrefTarget){
20749             el.target = this.hrefTarget;
20750         }
20751         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20752         
20753         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20754         
20755         el.innerHTML = String.format(
20756                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20757                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20758         this.el = el;
20759         Roo.menu.Item.superclass.onRender.call(this, container, position);
20760     },
20761
20762     /**
20763      * Sets the text to display in this menu item
20764      * @param {String} text The text to display
20765      * @param {Boolean} isHTML true to indicate text is pure html.
20766      */
20767     setText : function(text, isHTML){
20768         if (isHTML) {
20769             this.html = text;
20770         } else {
20771             this.text = text;
20772             this.html = '';
20773         }
20774         if(this.rendered){
20775             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20776      
20777             this.el.update(String.format(
20778                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20779                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20780             this.parentMenu.autoWidth();
20781         }
20782     },
20783
20784     // private
20785     handleClick : function(e){
20786         if(!this.href){ // if no link defined, stop the event automatically
20787             e.stopEvent();
20788         }
20789         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20790     },
20791
20792     // private
20793     activate : function(autoExpand){
20794         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20795             this.focus();
20796             if(autoExpand){
20797                 this.expandMenu();
20798             }
20799         }
20800         return true;
20801     },
20802
20803     // private
20804     shouldDeactivate : function(e){
20805         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20806             if(this.menu && this.menu.isVisible()){
20807                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20808             }
20809             return true;
20810         }
20811         return false;
20812     },
20813
20814     // private
20815     deactivate : function(){
20816         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20817         this.hideMenu();
20818     },
20819
20820     // private
20821     expandMenu : function(autoActivate){
20822         if(!this.disabled && this.menu){
20823             clearTimeout(this.hideTimer);
20824             delete this.hideTimer;
20825             if(!this.menu.isVisible() && !this.showTimer){
20826                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20827             }else if (this.menu.isVisible() && autoActivate){
20828                 this.menu.tryActivate(0, 1);
20829             }
20830         }
20831     },
20832
20833     // private
20834     deferExpand : function(autoActivate){
20835         delete this.showTimer;
20836         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20837         if(autoActivate){
20838             this.menu.tryActivate(0, 1);
20839         }
20840     },
20841
20842     // private
20843     hideMenu : function(){
20844         clearTimeout(this.showTimer);
20845         delete this.showTimer;
20846         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20847             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20848         }
20849     },
20850
20851     // private
20852     deferHide : function(){
20853         delete this.hideTimer;
20854         this.menu.hide();
20855     }
20856 });/*
20857  * Based on:
20858  * Ext JS Library 1.1.1
20859  * Copyright(c) 2006-2007, Ext JS, LLC.
20860  *
20861  * Originally Released Under LGPL - original licence link has changed is not relivant.
20862  *
20863  * Fork - LGPL
20864  * <script type="text/javascript">
20865  */
20866  
20867 /**
20868  * @class Roo.menu.CheckItem
20869  * @extends Roo.menu.Item
20870  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20871  * @constructor
20872  * Creates a new CheckItem
20873  * @param {Object} config Configuration options
20874  */
20875 Roo.menu.CheckItem = function(config){
20876     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20877     this.addEvents({
20878         /**
20879          * @event beforecheckchange
20880          * Fires before the checked value is set, providing an opportunity to cancel if needed
20881          * @param {Roo.menu.CheckItem} this
20882          * @param {Boolean} checked The new checked value that will be set
20883          */
20884         "beforecheckchange" : true,
20885         /**
20886          * @event checkchange
20887          * Fires after the checked value has been set
20888          * @param {Roo.menu.CheckItem} this
20889          * @param {Boolean} checked The checked value that was set
20890          */
20891         "checkchange" : true
20892     });
20893     if(this.checkHandler){
20894         this.on('checkchange', this.checkHandler, this.scope);
20895     }
20896 };
20897 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20898     /**
20899      * @cfg {String} group
20900      * All check items with the same group name will automatically be grouped into a single-select
20901      * radio button group (defaults to '')
20902      */
20903     /**
20904      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20905      */
20906     itemCls : "x-menu-item x-menu-check-item",
20907     /**
20908      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20909      */
20910     groupClass : "x-menu-group-item",
20911
20912     /**
20913      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20914      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20915      * initialized with checked = true will be rendered as checked.
20916      */
20917     checked: false,
20918
20919     // private
20920     ctype: "Roo.menu.CheckItem",
20921
20922     // private
20923     onRender : function(c){
20924         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20925         if(this.group){
20926             this.el.addClass(this.groupClass);
20927         }
20928         Roo.menu.MenuMgr.registerCheckable(this);
20929         if(this.checked){
20930             this.checked = false;
20931             this.setChecked(true, true);
20932         }
20933     },
20934
20935     // private
20936     destroy : function(){
20937         if(this.rendered){
20938             Roo.menu.MenuMgr.unregisterCheckable(this);
20939         }
20940         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20941     },
20942
20943     /**
20944      * Set the checked state of this item
20945      * @param {Boolean} checked The new checked value
20946      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20947      */
20948     setChecked : function(state, suppressEvent){
20949         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20950             if(this.container){
20951                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20952             }
20953             this.checked = state;
20954             if(suppressEvent !== true){
20955                 this.fireEvent("checkchange", this, state);
20956             }
20957         }
20958     },
20959
20960     // private
20961     handleClick : function(e){
20962        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20963            this.setChecked(!this.checked);
20964        }
20965        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20966     }
20967 });/*
20968  * Based on:
20969  * Ext JS Library 1.1.1
20970  * Copyright(c) 2006-2007, Ext JS, LLC.
20971  *
20972  * Originally Released Under LGPL - original licence link has changed is not relivant.
20973  *
20974  * Fork - LGPL
20975  * <script type="text/javascript">
20976  */
20977  
20978 /**
20979  * @class Roo.menu.DateItem
20980  * @extends Roo.menu.Adapter
20981  * A menu item that wraps the {@link Roo.DatPicker} component.
20982  * @constructor
20983  * Creates a new DateItem
20984  * @param {Object} config Configuration options
20985  */
20986 Roo.menu.DateItem = function(config){
20987     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20988     /** The Roo.DatePicker object @type Roo.DatePicker */
20989     this.picker = this.component;
20990     this.addEvents({select: true});
20991     
20992     this.picker.on("render", function(picker){
20993         picker.getEl().swallowEvent("click");
20994         picker.container.addClass("x-menu-date-item");
20995     });
20996
20997     this.picker.on("select", this.onSelect, this);
20998 };
20999
21000 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
21001     // private
21002     onSelect : function(picker, date){
21003         this.fireEvent("select", this, date, picker);
21004         Roo.menu.DateItem.superclass.handleClick.call(this);
21005     }
21006 });/*
21007  * Based on:
21008  * Ext JS Library 1.1.1
21009  * Copyright(c) 2006-2007, Ext JS, LLC.
21010  *
21011  * Originally Released Under LGPL - original licence link has changed is not relivant.
21012  *
21013  * Fork - LGPL
21014  * <script type="text/javascript">
21015  */
21016  
21017 /**
21018  * @class Roo.menu.ColorItem
21019  * @extends Roo.menu.Adapter
21020  * A menu item that wraps the {@link Roo.ColorPalette} component.
21021  * @constructor
21022  * Creates a new ColorItem
21023  * @param {Object} config Configuration options
21024  */
21025 Roo.menu.ColorItem = function(config){
21026     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
21027     /** The Roo.ColorPalette object @type Roo.ColorPalette */
21028     this.palette = this.component;
21029     this.relayEvents(this.palette, ["select"]);
21030     if(this.selectHandler){
21031         this.on('select', this.selectHandler, this.scope);
21032     }
21033 };
21034 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
21035  * Based on:
21036  * Ext JS Library 1.1.1
21037  * Copyright(c) 2006-2007, Ext JS, LLC.
21038  *
21039  * Originally Released Under LGPL - original licence link has changed is not relivant.
21040  *
21041  * Fork - LGPL
21042  * <script type="text/javascript">
21043  */
21044  
21045
21046 /**
21047  * @class Roo.menu.DateMenu
21048  * @extends Roo.menu.Menu
21049  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
21050  * @constructor
21051  * Creates a new DateMenu
21052  * @param {Object} config Configuration options
21053  */
21054 Roo.menu.DateMenu = function(config){
21055     Roo.menu.DateMenu.superclass.constructor.call(this, config);
21056     this.plain = true;
21057     var di = new Roo.menu.DateItem(config);
21058     this.add(di);
21059     /**
21060      * The {@link Roo.DatePicker} instance for this DateMenu
21061      * @type DatePicker
21062      */
21063     this.picker = di.picker;
21064     /**
21065      * @event select
21066      * @param {DatePicker} picker
21067      * @param {Date} date
21068      */
21069     this.relayEvents(di, ["select"]);
21070     this.on('beforeshow', function(){
21071         if(this.picker){
21072             this.picker.hideMonthPicker(false);
21073         }
21074     }, this);
21075 };
21076 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
21077     cls:'x-date-menu'
21078 });/*
21079  * Based on:
21080  * Ext JS Library 1.1.1
21081  * Copyright(c) 2006-2007, Ext JS, LLC.
21082  *
21083  * Originally Released Under LGPL - original licence link has changed is not relivant.
21084  *
21085  * Fork - LGPL
21086  * <script type="text/javascript">
21087  */
21088  
21089
21090 /**
21091  * @class Roo.menu.ColorMenu
21092  * @extends Roo.menu.Menu
21093  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
21094  * @constructor
21095  * Creates a new ColorMenu
21096  * @param {Object} config Configuration options
21097  */
21098 Roo.menu.ColorMenu = function(config){
21099     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
21100     this.plain = true;
21101     var ci = new Roo.menu.ColorItem(config);
21102     this.add(ci);
21103     /**
21104      * The {@link Roo.ColorPalette} instance for this ColorMenu
21105      * @type ColorPalette
21106      */
21107     this.palette = ci.palette;
21108     /**
21109      * @event select
21110      * @param {ColorPalette} palette
21111      * @param {String} color
21112      */
21113     this.relayEvents(ci, ["select"]);
21114 };
21115 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
21116  * Based on:
21117  * Ext JS Library 1.1.1
21118  * Copyright(c) 2006-2007, Ext JS, LLC.
21119  *
21120  * Originally Released Under LGPL - original licence link has changed is not relivant.
21121  *
21122  * Fork - LGPL
21123  * <script type="text/javascript">
21124  */
21125  
21126 /**
21127  * @class Roo.form.Field
21128  * @extends Roo.BoxComponent
21129  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
21130  * @constructor
21131  * Creates a new Field
21132  * @param {Object} config Configuration options
21133  */
21134 Roo.form.Field = function(config){
21135     Roo.form.Field.superclass.constructor.call(this, config);
21136 };
21137
21138 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
21139     /**
21140      * @cfg {String} fieldLabel Label to use when rendering a form.
21141      */
21142        /**
21143      * @cfg {String} qtip Mouse over tip
21144      */
21145      
21146     /**
21147      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
21148      */
21149     invalidClass : "x-form-invalid",
21150     /**
21151      * @cfg {String} invalidText The error text to use when marking a field invalid and no message is provided (defaults to "The value in this field is invalid")
21152      */
21153     invalidText : "The value in this field is invalid",
21154     /**
21155      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
21156      */
21157     focusClass : "x-form-focus",
21158     /**
21159      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
21160       automatic validation (defaults to "keyup").
21161      */
21162     validationEvent : "keyup",
21163     /**
21164      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
21165      */
21166     validateOnBlur : true,
21167     /**
21168      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
21169      */
21170     validationDelay : 250,
21171     /**
21172      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21173      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
21174      */
21175     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
21176     /**
21177      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
21178      */
21179     fieldClass : "x-form-field",
21180     /**
21181      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
21182      *<pre>
21183 Value         Description
21184 -----------   ----------------------------------------------------------------------
21185 qtip          Display a quick tip when the user hovers over the field
21186 title         Display a default browser title attribute popup
21187 under         Add a block div beneath the field containing the error text
21188 side          Add an error icon to the right of the field with a popup on hover
21189 [element id]  Add the error text directly to the innerHTML of the specified element
21190 </pre>
21191      */
21192     msgTarget : 'qtip',
21193     /**
21194      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
21195      */
21196     msgFx : 'normal',
21197
21198     /**
21199      * @cfg {Boolean} readOnly True to mark the field as readOnly in HTML (defaults to false) -- Note: this only sets the element's readOnly DOM attribute.
21200      */
21201     readOnly : false,
21202
21203     /**
21204      * @cfg {Boolean} disabled True to disable the field (defaults to false).
21205      */
21206     disabled : false,
21207
21208     /**
21209      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
21210      */
21211     inputType : undefined,
21212     
21213     /**
21214      * @cfg {Number} tabIndex The tabIndex for this field. Note this only applies to fields that are rendered, not those which are built via applyTo (defaults to undefined).
21215          */
21216         tabIndex : undefined,
21217         
21218     // private
21219     isFormField : true,
21220
21221     // private
21222     hasFocus : false,
21223     /**
21224      * @property {Roo.Element} fieldEl
21225      * Element Containing the rendered Field (with label etc.)
21226      */
21227     /**
21228      * @cfg {Mixed} value A value to initialize this field with.
21229      */
21230     value : undefined,
21231
21232     /**
21233      * @cfg {String} name The field's HTML name attribute.
21234      */
21235     /**
21236      * @cfg {String} cls A CSS class to apply to the field's underlying element.
21237      */
21238
21239         // private ??
21240         initComponent : function(){
21241         Roo.form.Field.superclass.initComponent.call(this);
21242         this.addEvents({
21243             /**
21244              * @event focus
21245              * Fires when this field receives input focus.
21246              * @param {Roo.form.Field} this
21247              */
21248             focus : true,
21249             /**
21250              * @event blur
21251              * Fires when this field loses input focus.
21252              * @param {Roo.form.Field} this
21253              */
21254             blur : true,
21255             /**
21256              * @event specialkey
21257              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
21258              * {@link Roo.EventObject#getKey} to determine which key was pressed.
21259              * @param {Roo.form.Field} this
21260              * @param {Roo.EventObject} e The event object
21261              */
21262             specialkey : true,
21263             /**
21264              * @event change
21265              * Fires just before the field blurs if the field value has changed.
21266              * @param {Roo.form.Field} this
21267              * @param {Mixed} newValue The new value
21268              * @param {Mixed} oldValue The original value
21269              */
21270             change : true,
21271             /**
21272              * @event invalid
21273              * Fires after the field has been marked as invalid.
21274              * @param {Roo.form.Field} this
21275              * @param {String} msg The validation message
21276              */
21277             invalid : true,
21278             /**
21279              * @event valid
21280              * Fires after the field has been validated with no errors.
21281              * @param {Roo.form.Field} this
21282              */
21283             valid : true,
21284              /**
21285              * @event keyup
21286              * Fires after the key up
21287              * @param {Roo.form.Field} this
21288              * @param {Roo.EventObject}  e The event Object
21289              */
21290             keyup : true
21291         });
21292     },
21293
21294     /**
21295      * Returns the name attribute of the field if available
21296      * @return {String} name The field name
21297      */
21298     getName: function(){
21299          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
21300     },
21301
21302     // private
21303     onRender : function(ct, position){
21304         Roo.form.Field.superclass.onRender.call(this, ct, position);
21305         if(!this.el){
21306             var cfg = this.getAutoCreate();
21307             if(!cfg.name){
21308                 cfg.name = this.name || this.id;
21309             }
21310             if(this.inputType){
21311                 cfg.type = this.inputType;
21312             }
21313             this.el = ct.createChild(cfg, position);
21314         }
21315         var type = this.el.dom.type;
21316         if(type){
21317             if(type == 'password'){
21318                 type = 'text';
21319             }
21320             this.el.addClass('x-form-'+type);
21321         }
21322         if(this.readOnly){
21323             this.el.dom.readOnly = true;
21324         }
21325         if(this.tabIndex !== undefined){
21326             this.el.dom.setAttribute('tabIndex', this.tabIndex);
21327         }
21328
21329         this.el.addClass([this.fieldClass, this.cls]);
21330         this.initValue();
21331     },
21332
21333     /**
21334      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
21335      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
21336      * @return {Roo.form.Field} this
21337      */
21338     applyTo : function(target){
21339         this.allowDomMove = false;
21340         this.el = Roo.get(target);
21341         this.render(this.el.dom.parentNode);
21342         return this;
21343     },
21344
21345     // private
21346     initValue : function(){
21347         if(this.value !== undefined){
21348             this.setValue(this.value);
21349         }else if(this.el.dom.value.length > 0){
21350             this.setValue(this.el.dom.value);
21351         }
21352     },
21353
21354     /**
21355      * Returns true if this field has been changed since it was originally loaded and is not disabled.
21356      */
21357     isDirty : function() {
21358         if(this.disabled) {
21359             return false;
21360         }
21361         return String(this.getValue()) !== String(this.originalValue);
21362     },
21363
21364     // private
21365     afterRender : function(){
21366         Roo.form.Field.superclass.afterRender.call(this);
21367         this.initEvents();
21368     },
21369
21370     // private
21371     fireKey : function(e){
21372         //Roo.log('field ' + e.getKey());
21373         if(e.isNavKeyPress()){
21374             this.fireEvent("specialkey", this, e);
21375         }
21376     },
21377
21378     /**
21379      * Resets the current field value to the originally loaded value and clears any validation messages
21380      */
21381     reset : function(){
21382         this.setValue(this.originalValue);
21383         this.clearInvalid();
21384     },
21385
21386     // private
21387     initEvents : function(){
21388         // safari killled keypress - so keydown is now used..
21389         this.el.on("keydown" , this.fireKey,  this);
21390         this.el.on("focus", this.onFocus,  this);
21391         this.el.on("blur", this.onBlur,  this);
21392         this.el.relayEvent('keyup', this);
21393
21394         // reference to original value for reset
21395         this.originalValue = this.getValue();
21396     },
21397
21398     // private
21399     onFocus : function(){
21400         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21401             this.el.addClass(this.focusClass);
21402         }
21403         if(!this.hasFocus){
21404             this.hasFocus = true;
21405             this.startValue = this.getValue();
21406             this.fireEvent("focus", this);
21407         }
21408     },
21409
21410     beforeBlur : Roo.emptyFn,
21411
21412     // private
21413     onBlur : function(){
21414         this.beforeBlur();
21415         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21416             this.el.removeClass(this.focusClass);
21417         }
21418         this.hasFocus = false;
21419         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21420             this.validate();
21421         }
21422         var v = this.getValue();
21423         if(String(v) !== String(this.startValue)){
21424             this.fireEvent('change', this, v, this.startValue);
21425         }
21426         this.fireEvent("blur", this);
21427     },
21428
21429     /**
21430      * Returns whether or not the field value is currently valid
21431      * @param {Boolean} preventMark True to disable marking the field invalid
21432      * @return {Boolean} True if the value is valid, else false
21433      */
21434     isValid : function(preventMark){
21435         if(this.disabled){
21436             return true;
21437         }
21438         var restore = this.preventMark;
21439         this.preventMark = preventMark === true;
21440         var v = this.validateValue(this.processValue(this.getRawValue()));
21441         this.preventMark = restore;
21442         return v;
21443     },
21444
21445     /**
21446      * Validates the field value
21447      * @return {Boolean} True if the value is valid, else false
21448      */
21449     validate : function(){
21450         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21451             this.clearInvalid();
21452             return true;
21453         }
21454         return false;
21455     },
21456
21457     processValue : function(value){
21458         return value;
21459     },
21460
21461     // private
21462     // Subclasses should provide the validation implementation by overriding this
21463     validateValue : function(value){
21464         return true;
21465     },
21466
21467     /**
21468      * Mark this field as invalid
21469      * @param {String} msg The validation message
21470      */
21471     markInvalid : function(msg){
21472         if(!this.rendered || this.preventMark){ // not rendered
21473             return;
21474         }
21475         this.el.addClass(this.invalidClass);
21476         msg = msg || this.invalidText;
21477         switch(this.msgTarget){
21478             case 'qtip':
21479                 this.el.dom.qtip = msg;
21480                 this.el.dom.qclass = 'x-form-invalid-tip';
21481                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21482                     Roo.QuickTips.enable();
21483                 }
21484                 break;
21485             case 'title':
21486                 this.el.dom.title = msg;
21487                 break;
21488             case 'under':
21489                 if(!this.errorEl){
21490                     var elp = this.el.findParent('.x-form-element', 5, true);
21491                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21492                     this.errorEl.setWidth(elp.getWidth(true)-20);
21493                 }
21494                 this.errorEl.update(msg);
21495                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21496                 break;
21497             case 'side':
21498                 if(!this.errorIcon){
21499                     var elp = this.el.findParent('.x-form-element', 5, true);
21500                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21501                 }
21502                 this.alignErrorIcon();
21503                 this.errorIcon.dom.qtip = msg;
21504                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21505                 this.errorIcon.show();
21506                 this.on('resize', this.alignErrorIcon, this);
21507                 break;
21508             default:
21509                 var t = Roo.getDom(this.msgTarget);
21510                 t.innerHTML = msg;
21511                 t.style.display = this.msgDisplay;
21512                 break;
21513         }
21514         this.fireEvent('invalid', this, msg);
21515     },
21516
21517     // private
21518     alignErrorIcon : function(){
21519         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21520     },
21521
21522     /**
21523      * Clear any invalid styles/messages for this field
21524      */
21525     clearInvalid : function(){
21526         if(!this.rendered || this.preventMark){ // not rendered
21527             return;
21528         }
21529         this.el.removeClass(this.invalidClass);
21530         switch(this.msgTarget){
21531             case 'qtip':
21532                 this.el.dom.qtip = '';
21533                 break;
21534             case 'title':
21535                 this.el.dom.title = '';
21536                 break;
21537             case 'under':
21538                 if(this.errorEl){
21539                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21540                 }
21541                 break;
21542             case 'side':
21543                 if(this.errorIcon){
21544                     this.errorIcon.dom.qtip = '';
21545                     this.errorIcon.hide();
21546                     this.un('resize', this.alignErrorIcon, this);
21547                 }
21548                 break;
21549             default:
21550                 var t = Roo.getDom(this.msgTarget);
21551                 t.innerHTML = '';
21552                 t.style.display = 'none';
21553                 break;
21554         }
21555         this.fireEvent('valid', this);
21556     },
21557
21558     /**
21559      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21560      * @return {Mixed} value The field value
21561      */
21562     getRawValue : function(){
21563         var v = this.el.getValue();
21564         if(v === this.emptyText){
21565             v = '';
21566         }
21567         return v;
21568     },
21569
21570     /**
21571      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21572      * @return {Mixed} value The field value
21573      */
21574     getValue : function(){
21575         var v = this.el.getValue();
21576         if(v === this.emptyText || v === undefined){
21577             v = '';
21578         }
21579         return v;
21580     },
21581
21582     /**
21583      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21584      * @param {Mixed} value The value to set
21585      */
21586     setRawValue : function(v){
21587         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21588     },
21589
21590     /**
21591      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21592      * @param {Mixed} value The value to set
21593      */
21594     setValue : function(v){
21595         this.value = v;
21596         if(this.rendered){
21597             this.el.dom.value = (v === null || v === undefined ? '' : v);
21598              this.validate();
21599         }
21600     },
21601
21602     adjustSize : function(w, h){
21603         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21604         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21605         return s;
21606     },
21607
21608     adjustWidth : function(tag, w){
21609         tag = tag.toLowerCase();
21610         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21611             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21612                 if(tag == 'input'){
21613                     return w + 2;
21614                 }
21615                 if(tag = 'textarea'){
21616                     return w-2;
21617                 }
21618             }else if(Roo.isOpera){
21619                 if(tag == 'input'){
21620                     return w + 2;
21621                 }
21622                 if(tag = 'textarea'){
21623                     return w-2;
21624                 }
21625             }
21626         }
21627         return w;
21628     }
21629 });
21630
21631
21632 // anything other than normal should be considered experimental
21633 Roo.form.Field.msgFx = {
21634     normal : {
21635         show: function(msgEl, f){
21636             msgEl.setDisplayed('block');
21637         },
21638
21639         hide : function(msgEl, f){
21640             msgEl.setDisplayed(false).update('');
21641         }
21642     },
21643
21644     slide : {
21645         show: function(msgEl, f){
21646             msgEl.slideIn('t', {stopFx:true});
21647         },
21648
21649         hide : function(msgEl, f){
21650             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21651         }
21652     },
21653
21654     slideRight : {
21655         show: function(msgEl, f){
21656             msgEl.fixDisplay();
21657             msgEl.alignTo(f.el, 'tl-tr');
21658             msgEl.slideIn('l', {stopFx:true});
21659         },
21660
21661         hide : function(msgEl, f){
21662             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21663         }
21664     }
21665 };/*
21666  * Based on:
21667  * Ext JS Library 1.1.1
21668  * Copyright(c) 2006-2007, Ext JS, LLC.
21669  *
21670  * Originally Released Under LGPL - original licence link has changed is not relivant.
21671  *
21672  * Fork - LGPL
21673  * <script type="text/javascript">
21674  */
21675  
21676
21677 /**
21678  * @class Roo.form.TextField
21679  * @extends Roo.form.Field
21680  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21681  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21682  * @constructor
21683  * Creates a new TextField
21684  * @param {Object} config Configuration options
21685  */
21686 Roo.form.TextField = function(config){
21687     Roo.form.TextField.superclass.constructor.call(this, config);
21688     this.addEvents({
21689         /**
21690          * @event autosize
21691          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21692          * according to the default logic, but this event provides a hook for the developer to apply additional
21693          * logic at runtime to resize the field if needed.
21694              * @param {Roo.form.Field} this This text field
21695              * @param {Number} width The new field width
21696              */
21697         autosize : true
21698     });
21699 };
21700
21701 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21702     /**
21703      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21704      */
21705     grow : false,
21706     /**
21707      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21708      */
21709     growMin : 30,
21710     /**
21711      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21712      */
21713     growMax : 800,
21714     /**
21715      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21716      */
21717     vtype : null,
21718     /**
21719      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21720      */
21721     maskRe : null,
21722     /**
21723      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21724      */
21725     disableKeyFilter : false,
21726     /**
21727      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21728      */
21729     allowBlank : true,
21730     /**
21731      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21732      */
21733     minLength : 0,
21734     /**
21735      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21736      */
21737     maxLength : Number.MAX_VALUE,
21738     /**
21739      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21740      */
21741     minLengthText : "The minimum length for this field is {0}",
21742     /**
21743      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21744      */
21745     maxLengthText : "The maximum length for this field is {0}",
21746     /**
21747      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21748      */
21749     selectOnFocus : false,
21750     /**
21751      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21752      */
21753     blankText : "This field is required",
21754     /**
21755      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21756      * If available, this function will be called only after the basic validators all return true, and will be passed the
21757      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21758      */
21759     validator : null,
21760     /**
21761      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21762      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21763      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21764      */
21765     regex : null,
21766     /**
21767      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21768      */
21769     regexText : "",
21770     /**
21771      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
21772      */
21773     emptyText : null,
21774     /**
21775      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
21776      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
21777      */
21778     emptyClass : 'x-form-empty-field',
21779
21780     // private
21781     initEvents : function(){
21782         Roo.form.TextField.superclass.initEvents.call(this);
21783         if(this.validationEvent == 'keyup'){
21784             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21785             this.el.on('keyup', this.filterValidation, this);
21786         }
21787         else if(this.validationEvent !== false){
21788             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21789         }
21790         if(this.selectOnFocus || this.emptyText){
21791             this.on("focus", this.preFocus, this);
21792             if(this.emptyText){
21793                 this.on('blur', this.postBlur, this);
21794                 this.applyEmptyText();
21795             }
21796         }
21797         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21798             this.el.on("keypress", this.filterKeys, this);
21799         }
21800         if(this.grow){
21801             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21802             this.el.on("click", this.autoSize,  this);
21803         }
21804     },
21805
21806     processValue : function(value){
21807         if(this.stripCharsRe){
21808             var newValue = value.replace(this.stripCharsRe, '');
21809             if(newValue !== value){
21810                 this.setRawValue(newValue);
21811                 return newValue;
21812             }
21813         }
21814         return value;
21815     },
21816
21817     filterValidation : function(e){
21818         if(!e.isNavKeyPress()){
21819             this.validationTask.delay(this.validationDelay);
21820         }
21821     },
21822
21823     // private
21824     onKeyUp : function(e){
21825         if(!e.isNavKeyPress()){
21826             this.autoSize();
21827         }
21828     },
21829
21830     /**
21831      * Resets the current field value to the originally-loaded value and clears any validation messages.
21832      * Also adds emptyText and emptyClass if the original value was blank.
21833      */
21834     reset : function(){
21835         Roo.form.TextField.superclass.reset.call(this);
21836         this.applyEmptyText();
21837     },
21838
21839     applyEmptyText : function(){
21840         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
21841             this.setRawValue(this.emptyText);
21842             this.el.addClass(this.emptyClass);
21843         }
21844     },
21845
21846     // private
21847     preFocus : function(){
21848         if(this.emptyText){
21849             if(this.el.dom.value == this.emptyText){
21850                 this.setRawValue('');
21851             }
21852             this.el.removeClass(this.emptyClass);
21853         }
21854         if(this.selectOnFocus){
21855             this.el.dom.select();
21856         }
21857     },
21858
21859     // private
21860     postBlur : function(){
21861         this.applyEmptyText();
21862     },
21863
21864     // private
21865     filterKeys : function(e){
21866         var k = e.getKey();
21867         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21868             return;
21869         }
21870         var c = e.getCharCode(), cc = String.fromCharCode(c);
21871         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21872             return;
21873         }
21874         if(!this.maskRe.test(cc)){
21875             e.stopEvent();
21876         }
21877     },
21878
21879     setValue : function(v){
21880         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
21881             this.el.removeClass(this.emptyClass);
21882         }
21883         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21884         this.applyEmptyText();
21885         this.autoSize();
21886     },
21887
21888     /**
21889      * Validates a value according to the field's validation rules and marks the field as invalid
21890      * if the validation fails
21891      * @param {Mixed} value The value to validate
21892      * @return {Boolean} True if the value is valid, else false
21893      */
21894     validateValue : function(value){
21895         if(value.length < 1 || value === this.emptyText){ // if it's blank
21896              if(this.allowBlank){
21897                 this.clearInvalid();
21898                 return true;
21899              }else{
21900                 this.markInvalid(this.blankText);
21901                 return false;
21902              }
21903         }
21904         if(value.length < this.minLength){
21905             this.markInvalid(String.format(this.minLengthText, this.minLength));
21906             return false;
21907         }
21908         if(value.length > this.maxLength){
21909             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21910             return false;
21911         }
21912         if(this.vtype){
21913             var vt = Roo.form.VTypes;
21914             if(!vt[this.vtype](value, this)){
21915                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21916                 return false;
21917             }
21918         }
21919         if(typeof this.validator == "function"){
21920             var msg = this.validator(value);
21921             if(msg !== true){
21922                 this.markInvalid(msg);
21923                 return false;
21924             }
21925         }
21926         if(this.regex && !this.regex.test(value)){
21927             this.markInvalid(this.regexText);
21928             return false;
21929         }
21930         return true;
21931     },
21932
21933     /**
21934      * Selects text in this field
21935      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21936      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21937      */
21938     selectText : function(start, end){
21939         var v = this.getRawValue();
21940         if(v.length > 0){
21941             start = start === undefined ? 0 : start;
21942             end = end === undefined ? v.length : end;
21943             var d = this.el.dom;
21944             if(d.setSelectionRange){
21945                 d.setSelectionRange(start, end);
21946             }else if(d.createTextRange){
21947                 var range = d.createTextRange();
21948                 range.moveStart("character", start);
21949                 range.moveEnd("character", v.length-end);
21950                 range.select();
21951             }
21952         }
21953     },
21954
21955     /**
21956      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21957      * This only takes effect if grow = true, and fires the autosize event.
21958      */
21959     autoSize : function(){
21960         if(!this.grow || !this.rendered){
21961             return;
21962         }
21963         if(!this.metrics){
21964             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21965         }
21966         var el = this.el;
21967         var v = el.dom.value;
21968         var d = document.createElement('div');
21969         d.appendChild(document.createTextNode(v));
21970         v = d.innerHTML;
21971         d = null;
21972         v += "&#160;";
21973         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21974         this.el.setWidth(w);
21975         this.fireEvent("autosize", this, w);
21976     }
21977 });/*
21978  * Based on:
21979  * Ext JS Library 1.1.1
21980  * Copyright(c) 2006-2007, Ext JS, LLC.
21981  *
21982  * Originally Released Under LGPL - original licence link has changed is not relivant.
21983  *
21984  * Fork - LGPL
21985  * <script type="text/javascript">
21986  */
21987  
21988 /**
21989  * @class Roo.form.Hidden
21990  * @extends Roo.form.TextField
21991  * Simple Hidden element used on forms 
21992  * 
21993  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21994  * 
21995  * @constructor
21996  * Creates a new Hidden form element.
21997  * @param {Object} config Configuration options
21998  */
21999
22000
22001
22002 // easy hidden field...
22003 Roo.form.Hidden = function(config){
22004     Roo.form.Hidden.superclass.constructor.call(this, config);
22005 };
22006   
22007 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
22008     fieldLabel:      '',
22009     inputType:      'hidden',
22010     width:          50,
22011     allowBlank:     true,
22012     labelSeparator: '',
22013     hidden:         true,
22014     itemCls :       'x-form-item-display-none'
22015
22016
22017 });
22018
22019
22020 /*
22021  * Based on:
22022  * Ext JS Library 1.1.1
22023  * Copyright(c) 2006-2007, Ext JS, LLC.
22024  *
22025  * Originally Released Under LGPL - original licence link has changed is not relivant.
22026  *
22027  * Fork - LGPL
22028  * <script type="text/javascript">
22029  */
22030  
22031 /**
22032  * @class Roo.form.TriggerField
22033  * @extends Roo.form.TextField
22034  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
22035  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
22036  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
22037  * for which you can provide a custom implementation.  For example:
22038  * <pre><code>
22039 var trigger = new Roo.form.TriggerField();
22040 trigger.onTriggerClick = myTriggerFn;
22041 trigger.applyTo('my-field');
22042 </code></pre>
22043  *
22044  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
22045  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
22046  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22047  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
22048  * @constructor
22049  * Create a new TriggerField.
22050  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
22051  * to the base TextField)
22052  */
22053 Roo.form.TriggerField = function(config){
22054     this.mimicing = false;
22055     Roo.form.TriggerField.superclass.constructor.call(this, config);
22056 };
22057
22058 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
22059     /**
22060      * @cfg {String} triggerClass A CSS class to apply to the trigger
22061      */
22062     /**
22063      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22064      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
22065      */
22066     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
22067     /**
22068      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
22069      */
22070     hideTrigger:false,
22071
22072     /** @cfg {Boolean} grow @hide */
22073     /** @cfg {Number} growMin @hide */
22074     /** @cfg {Number} growMax @hide */
22075
22076     /**
22077      * @hide 
22078      * @method
22079      */
22080     autoSize: Roo.emptyFn,
22081     // private
22082     monitorTab : true,
22083     // private
22084     deferHeight : true,
22085
22086     
22087     actionMode : 'wrap',
22088     // private
22089     onResize : function(w, h){
22090         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
22091         if(typeof w == 'number'){
22092             var x = w - this.trigger.getWidth();
22093             this.el.setWidth(this.adjustWidth('input', x));
22094             this.trigger.setStyle('left', x+'px');
22095         }
22096     },
22097
22098     // private
22099     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22100
22101     // private
22102     getResizeEl : function(){
22103         return this.wrap;
22104     },
22105
22106     // private
22107     getPositionEl : function(){
22108         return this.wrap;
22109     },
22110
22111     // private
22112     alignErrorIcon : function(){
22113         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
22114     },
22115
22116     // private
22117     onRender : function(ct, position){
22118         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
22119         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
22120         this.trigger = this.wrap.createChild(this.triggerConfig ||
22121                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
22122         if(this.hideTrigger){
22123             this.trigger.setDisplayed(false);
22124         }
22125         this.initTrigger();
22126         if(!this.width){
22127             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
22128         }
22129     },
22130
22131     // private
22132     initTrigger : function(){
22133         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
22134         this.trigger.addClassOnOver('x-form-trigger-over');
22135         this.trigger.addClassOnClick('x-form-trigger-click');
22136     },
22137
22138     // private
22139     onDestroy : function(){
22140         if(this.trigger){
22141             this.trigger.removeAllListeners();
22142             this.trigger.remove();
22143         }
22144         if(this.wrap){
22145             this.wrap.remove();
22146         }
22147         Roo.form.TriggerField.superclass.onDestroy.call(this);
22148     },
22149
22150     // private
22151     onFocus : function(){
22152         Roo.form.TriggerField.superclass.onFocus.call(this);
22153         if(!this.mimicing){
22154             this.wrap.addClass('x-trigger-wrap-focus');
22155             this.mimicing = true;
22156             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
22157             if(this.monitorTab){
22158                 this.el.on("keydown", this.checkTab, this);
22159             }
22160         }
22161     },
22162
22163     // private
22164     checkTab : function(e){
22165         if(e.getKey() == e.TAB){
22166             this.triggerBlur();
22167         }
22168     },
22169
22170     // private
22171     onBlur : function(){
22172         // do nothing
22173     },
22174
22175     // private
22176     mimicBlur : function(e, t){
22177         if(!this.wrap.contains(t) && this.validateBlur()){
22178             this.triggerBlur();
22179         }
22180     },
22181
22182     // private
22183     triggerBlur : function(){
22184         this.mimicing = false;
22185         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
22186         if(this.monitorTab){
22187             this.el.un("keydown", this.checkTab, this);
22188         }
22189         this.wrap.removeClass('x-trigger-wrap-focus');
22190         Roo.form.TriggerField.superclass.onBlur.call(this);
22191     },
22192
22193     // private
22194     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
22195     validateBlur : function(e, t){
22196         return true;
22197     },
22198
22199     // private
22200     onDisable : function(){
22201         Roo.form.TriggerField.superclass.onDisable.call(this);
22202         if(this.wrap){
22203             this.wrap.addClass('x-item-disabled');
22204         }
22205     },
22206
22207     // private
22208     onEnable : function(){
22209         Roo.form.TriggerField.superclass.onEnable.call(this);
22210         if(this.wrap){
22211             this.wrap.removeClass('x-item-disabled');
22212         }
22213     },
22214
22215     // private
22216     onShow : function(){
22217         var ae = this.getActionEl();
22218         
22219         if(ae){
22220             ae.dom.style.display = '';
22221             ae.dom.style.visibility = 'visible';
22222         }
22223     },
22224
22225     // private
22226     
22227     onHide : function(){
22228         var ae = this.getActionEl();
22229         ae.dom.style.display = 'none';
22230     },
22231
22232     /**
22233      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
22234      * by an implementing function.
22235      * @method
22236      * @param {EventObject} e
22237      */
22238     onTriggerClick : Roo.emptyFn
22239 });
22240
22241 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
22242 // to be extended by an implementing class.  For an example of implementing this class, see the custom
22243 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
22244 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
22245     initComponent : function(){
22246         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
22247
22248         this.triggerConfig = {
22249             tag:'span', cls:'x-form-twin-triggers', cn:[
22250             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
22251             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
22252         ]};
22253     },
22254
22255     getTrigger : function(index){
22256         return this.triggers[index];
22257     },
22258
22259     initTrigger : function(){
22260         var ts = this.trigger.select('.x-form-trigger', true);
22261         this.wrap.setStyle('overflow', 'hidden');
22262         var triggerField = this;
22263         ts.each(function(t, all, index){
22264             t.hide = function(){
22265                 var w = triggerField.wrap.getWidth();
22266                 this.dom.style.display = 'none';
22267                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22268             };
22269             t.show = function(){
22270                 var w = triggerField.wrap.getWidth();
22271                 this.dom.style.display = '';
22272                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22273             };
22274             var triggerIndex = 'Trigger'+(index+1);
22275
22276             if(this['hide'+triggerIndex]){
22277                 t.dom.style.display = 'none';
22278             }
22279             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
22280             t.addClassOnOver('x-form-trigger-over');
22281             t.addClassOnClick('x-form-trigger-click');
22282         }, this);
22283         this.triggers = ts.elements;
22284     },
22285
22286     onTrigger1Click : Roo.emptyFn,
22287     onTrigger2Click : Roo.emptyFn
22288 });/*
22289  * Based on:
22290  * Ext JS Library 1.1.1
22291  * Copyright(c) 2006-2007, Ext JS, LLC.
22292  *
22293  * Originally Released Under LGPL - original licence link has changed is not relivant.
22294  *
22295  * Fork - LGPL
22296  * <script type="text/javascript">
22297  */
22298  
22299 /**
22300  * @class Roo.form.TextArea
22301  * @extends Roo.form.TextField
22302  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
22303  * support for auto-sizing.
22304  * @constructor
22305  * Creates a new TextArea
22306  * @param {Object} config Configuration options
22307  */
22308 Roo.form.TextArea = function(config){
22309     Roo.form.TextArea.superclass.constructor.call(this, config);
22310     // these are provided exchanges for backwards compat
22311     // minHeight/maxHeight were replaced by growMin/growMax to be
22312     // compatible with TextField growing config values
22313     if(this.minHeight !== undefined){
22314         this.growMin = this.minHeight;
22315     }
22316     if(this.maxHeight !== undefined){
22317         this.growMax = this.maxHeight;
22318     }
22319 };
22320
22321 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
22322     /**
22323      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
22324      */
22325     growMin : 60,
22326     /**
22327      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
22328      */
22329     growMax: 1000,
22330     /**
22331      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
22332      * in the field (equivalent to setting overflow: hidden, defaults to false)
22333      */
22334     preventScrollbars: false,
22335     /**
22336      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22337      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
22338      */
22339
22340     // private
22341     onRender : function(ct, position){
22342         if(!this.el){
22343             this.defaultAutoCreate = {
22344                 tag: "textarea",
22345                 style:"width:300px;height:60px;",
22346                 autocomplete: "off"
22347             };
22348         }
22349         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
22350         if(this.grow){
22351             this.textSizeEl = Roo.DomHelper.append(document.body, {
22352                 tag: "pre", cls: "x-form-grow-sizer"
22353             });
22354             if(this.preventScrollbars){
22355                 this.el.setStyle("overflow", "hidden");
22356             }
22357             this.el.setHeight(this.growMin);
22358         }
22359     },
22360
22361     onDestroy : function(){
22362         if(this.textSizeEl){
22363             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
22364         }
22365         Roo.form.TextArea.superclass.onDestroy.call(this);
22366     },
22367
22368     // private
22369     onKeyUp : function(e){
22370         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
22371             this.autoSize();
22372         }
22373     },
22374
22375     /**
22376      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
22377      * This only takes effect if grow = true, and fires the autosize event if the height changes.
22378      */
22379     autoSize : function(){
22380         if(!this.grow || !this.textSizeEl){
22381             return;
22382         }
22383         var el = this.el;
22384         var v = el.dom.value;
22385         var ts = this.textSizeEl;
22386
22387         ts.innerHTML = '';
22388         ts.appendChild(document.createTextNode(v));
22389         v = ts.innerHTML;
22390
22391         Roo.fly(ts).setWidth(this.el.getWidth());
22392         if(v.length < 1){
22393             v = "&#160;&#160;";
22394         }else{
22395             if(Roo.isIE){
22396                 v = v.replace(/\n/g, '<p>&#160;</p>');
22397             }
22398             v += "&#160;\n&#160;";
22399         }
22400         ts.innerHTML = v;
22401         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
22402         if(h != this.lastHeight){
22403             this.lastHeight = h;
22404             this.el.setHeight(h);
22405             this.fireEvent("autosize", this, h);
22406         }
22407     }
22408 });/*
22409  * Based on:
22410  * Ext JS Library 1.1.1
22411  * Copyright(c) 2006-2007, Ext JS, LLC.
22412  *
22413  * Originally Released Under LGPL - original licence link has changed is not relivant.
22414  *
22415  * Fork - LGPL
22416  * <script type="text/javascript">
22417  */
22418  
22419
22420 /**
22421  * @class Roo.form.NumberField
22422  * @extends Roo.form.TextField
22423  * Numeric text field that provides automatic keystroke filtering and numeric validation.
22424  * @constructor
22425  * Creates a new NumberField
22426  * @param {Object} config Configuration options
22427  */
22428 Roo.form.NumberField = function(config){
22429     Roo.form.NumberField.superclass.constructor.call(this, config);
22430 };
22431
22432 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
22433     /**
22434      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22435      */
22436     fieldClass: "x-form-field x-form-num-field",
22437     /**
22438      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22439      */
22440     allowDecimals : true,
22441     /**
22442      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22443      */
22444     decimalSeparator : ".",
22445     /**
22446      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22447      */
22448     decimalPrecision : 2,
22449     /**
22450      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22451      */
22452     allowNegative : true,
22453     /**
22454      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22455      */
22456     minValue : Number.NEGATIVE_INFINITY,
22457     /**
22458      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22459      */
22460     maxValue : Number.MAX_VALUE,
22461     /**
22462      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22463      */
22464     minText : "The minimum value for this field is {0}",
22465     /**
22466      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22467      */
22468     maxText : "The maximum value for this field is {0}",
22469     /**
22470      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22471      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22472      */
22473     nanText : "{0} is not a valid number",
22474
22475     // private
22476     initEvents : function(){
22477         Roo.form.NumberField.superclass.initEvents.call(this);
22478         var allowed = "0123456789";
22479         if(this.allowDecimals){
22480             allowed += this.decimalSeparator;
22481         }
22482         if(this.allowNegative){
22483             allowed += "-";
22484         }
22485         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22486         var keyPress = function(e){
22487             var k = e.getKey();
22488             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22489                 return;
22490             }
22491             var c = e.getCharCode();
22492             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22493                 e.stopEvent();
22494             }
22495         };
22496         this.el.on("keypress", keyPress, this);
22497     },
22498
22499     // private
22500     validateValue : function(value){
22501         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22502             return false;
22503         }
22504         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22505              return true;
22506         }
22507         var num = this.parseValue(value);
22508         if(isNaN(num)){
22509             this.markInvalid(String.format(this.nanText, value));
22510             return false;
22511         }
22512         if(num < this.minValue){
22513             this.markInvalid(String.format(this.minText, this.minValue));
22514             return false;
22515         }
22516         if(num > this.maxValue){
22517             this.markInvalid(String.format(this.maxText, this.maxValue));
22518             return false;
22519         }
22520         return true;
22521     },
22522
22523     getValue : function(){
22524         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22525     },
22526
22527     // private
22528     parseValue : function(value){
22529         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22530         return isNaN(value) ? '' : value;
22531     },
22532
22533     // private
22534     fixPrecision : function(value){
22535         var nan = isNaN(value);
22536         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22537             return nan ? '' : value;
22538         }
22539         return parseFloat(value).toFixed(this.decimalPrecision);
22540     },
22541
22542     setValue : function(v){
22543         v = this.fixPrecision(v);
22544         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22545     },
22546
22547     // private
22548     decimalPrecisionFcn : function(v){
22549         return Math.floor(v);
22550     },
22551
22552     beforeBlur : function(){
22553         var v = this.parseValue(this.getRawValue());
22554         if(v){
22555             this.setValue(v);
22556         }
22557     }
22558 });/*
22559  * Based on:
22560  * Ext JS Library 1.1.1
22561  * Copyright(c) 2006-2007, Ext JS, LLC.
22562  *
22563  * Originally Released Under LGPL - original licence link has changed is not relivant.
22564  *
22565  * Fork - LGPL
22566  * <script type="text/javascript">
22567  */
22568  
22569 /**
22570  * @class Roo.form.DateField
22571  * @extends Roo.form.TriggerField
22572  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22573 * @constructor
22574 * Create a new DateField
22575 * @param {Object} config
22576  */
22577 Roo.form.DateField = function(config){
22578     Roo.form.DateField.superclass.constructor.call(this, config);
22579     
22580       this.addEvents({
22581          
22582         /**
22583          * @event select
22584          * Fires when a date is selected
22585              * @param {Roo.form.DateField} combo This combo box
22586              * @param {Date} date The date selected
22587              */
22588         'select' : true
22589          
22590     });
22591     
22592     
22593     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22594     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22595     this.ddMatch = null;
22596     if(this.disabledDates){
22597         var dd = this.disabledDates;
22598         var re = "(?:";
22599         for(var i = 0; i < dd.length; i++){
22600             re += dd[i];
22601             if(i != dd.length-1) re += "|";
22602         }
22603         this.ddMatch = new RegExp(re + ")");
22604     }
22605 };
22606
22607 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22608     /**
22609      * @cfg {String} format
22610      * The default date format string which can be overriden for localization support.  The format must be
22611      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22612      */
22613     format : "m/d/y",
22614     /**
22615      * @cfg {String} altFormats
22616      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22617      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22618      */
22619     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22620     /**
22621      * @cfg {Array} disabledDays
22622      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22623      */
22624     disabledDays : null,
22625     /**
22626      * @cfg {String} disabledDaysText
22627      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22628      */
22629     disabledDaysText : "Disabled",
22630     /**
22631      * @cfg {Array} disabledDates
22632      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22633      * expression so they are very powerful. Some examples:
22634      * <ul>
22635      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22636      * <li>["03/08", "09/16"] would disable those days for every year</li>
22637      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22638      * <li>["03/../2006"] would disable every day in March 2006</li>
22639      * <li>["^03"] would disable every day in every March</li>
22640      * </ul>
22641      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22642      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22643      */
22644     disabledDates : null,
22645     /**
22646      * @cfg {String} disabledDatesText
22647      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22648      */
22649     disabledDatesText : "Disabled",
22650     /**
22651      * @cfg {Date/String} minValue
22652      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22653      * valid format (defaults to null).
22654      */
22655     minValue : null,
22656     /**
22657      * @cfg {Date/String} maxValue
22658      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22659      * valid format (defaults to null).
22660      */
22661     maxValue : null,
22662     /**
22663      * @cfg {String} minText
22664      * The error text to display when the date in the cell is before minValue (defaults to
22665      * 'The date in this field must be after {minValue}').
22666      */
22667     minText : "The date in this field must be equal to or after {0}",
22668     /**
22669      * @cfg {String} maxText
22670      * The error text to display when the date in the cell is after maxValue (defaults to
22671      * 'The date in this field must be before {maxValue}').
22672      */
22673     maxText : "The date in this field must be equal to or before {0}",
22674     /**
22675      * @cfg {String} invalidText
22676      * The error text to display when the date in the field is invalid (defaults to
22677      * '{value} is not a valid date - it must be in the format {format}').
22678      */
22679     invalidText : "{0} is not a valid date - it must be in the format {1}",
22680     /**
22681      * @cfg {String} triggerClass
22682      * An additional CSS class used to style the trigger button.  The trigger will always get the
22683      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22684      * which displays a calendar icon).
22685      */
22686     triggerClass : 'x-form-date-trigger',
22687     
22688
22689     /**
22690      * @cfg {Boolean} useIso
22691      * if enabled, then the date field will use a hidden field to store the 
22692      * real value as iso formated date. default (false)
22693      */ 
22694     useIso : false,
22695     /**
22696      * @cfg {String/Object} autoCreate
22697      * A DomHelper element spec, or true for a default element spec (defaults to
22698      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22699      */ 
22700     // private
22701     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22702     
22703     // private
22704     hiddenField: false,
22705     
22706     onRender : function(ct, position)
22707     {
22708         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22709         if (this.useIso) {
22710             this.el.dom.removeAttribute('name'); 
22711             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22712                     'before', true);
22713             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22714             // prevent input submission
22715             this.hiddenName = this.name;
22716         }
22717             
22718             
22719     },
22720     
22721     // private
22722     validateValue : function(value)
22723     {
22724         value = this.formatDate(value);
22725         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22726             return false;
22727         }
22728         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22729              return true;
22730         }
22731         var svalue = value;
22732         value = this.parseDate(value);
22733         if(!value){
22734             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22735             return false;
22736         }
22737         var time = value.getTime();
22738         if(this.minValue && time < this.minValue.getTime()){
22739             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22740             return false;
22741         }
22742         if(this.maxValue && time > this.maxValue.getTime()){
22743             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22744             return false;
22745         }
22746         if(this.disabledDays){
22747             var day = value.getDay();
22748             for(var i = 0; i < this.disabledDays.length; i++) {
22749                 if(day === this.disabledDays[i]){
22750                     this.markInvalid(this.disabledDaysText);
22751                     return false;
22752                 }
22753             }
22754         }
22755         var fvalue = this.formatDate(value);
22756         if(this.ddMatch && this.ddMatch.test(fvalue)){
22757             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22758             return false;
22759         }
22760         return true;
22761     },
22762
22763     // private
22764     // Provides logic to override the default TriggerField.validateBlur which just returns true
22765     validateBlur : function(){
22766         return !this.menu || !this.menu.isVisible();
22767     },
22768
22769     /**
22770      * Returns the current date value of the date field.
22771      * @return {Date} The date value
22772      */
22773     getValue : function(){
22774         
22775         return  this.hiddenField ?
22776                 this.hiddenField.value :
22777                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22778     },
22779
22780     /**
22781      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22782      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22783      * (the default format used is "m/d/y").
22784      * <br />Usage:
22785      * <pre><code>
22786 //All of these calls set the same date value (May 4, 2006)
22787
22788 //Pass a date object:
22789 var dt = new Date('5/4/06');
22790 dateField.setValue(dt);
22791
22792 //Pass a date string (default format):
22793 dateField.setValue('5/4/06');
22794
22795 //Pass a date string (custom format):
22796 dateField.format = 'Y-m-d';
22797 dateField.setValue('2006-5-4');
22798 </code></pre>
22799      * @param {String/Date} date The date or valid date string
22800      */
22801     setValue : function(date){
22802         if (this.hiddenField) {
22803             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22804         }
22805         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22806         // make sure the value field is always stored as a date..
22807         this.value = this.parseDate(date);
22808         
22809         
22810     },
22811
22812     // private
22813     parseDate : function(value){
22814         if(!value || value instanceof Date){
22815             return value;
22816         }
22817         var v = Date.parseDate(value, this.format);
22818          if (this.useIso) {
22819             v = Date.parseDate(value, 'Y-m-d');
22820         }
22821         if(!v && this.altFormats){
22822             if(!this.altFormatsArray){
22823                 this.altFormatsArray = this.altFormats.split("|");
22824             }
22825             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22826                 v = Date.parseDate(value, this.altFormatsArray[i]);
22827             }
22828         }
22829         return v;
22830     },
22831
22832     // private
22833     formatDate : function(date, fmt){
22834         return (!date || !(date instanceof Date)) ?
22835                date : date.dateFormat(fmt || this.format);
22836     },
22837
22838     // private
22839     menuListeners : {
22840         select: function(m, d){
22841             this.setValue(d);
22842             this.fireEvent('select', this, d);
22843         },
22844         show : function(){ // retain focus styling
22845             this.onFocus();
22846         },
22847         hide : function(){
22848             this.focus.defer(10, this);
22849             var ml = this.menuListeners;
22850             this.menu.un("select", ml.select,  this);
22851             this.menu.un("show", ml.show,  this);
22852             this.menu.un("hide", ml.hide,  this);
22853         }
22854     },
22855
22856     // private
22857     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22858     onTriggerClick : function(){
22859         if(this.disabled){
22860             return;
22861         }
22862         if(this.menu == null){
22863             this.menu = new Roo.menu.DateMenu();
22864         }
22865         Roo.apply(this.menu.picker,  {
22866             showClear: this.allowBlank,
22867             minDate : this.minValue,
22868             maxDate : this.maxValue,
22869             disabledDatesRE : this.ddMatch,
22870             disabledDatesText : this.disabledDatesText,
22871             disabledDays : this.disabledDays,
22872             disabledDaysText : this.disabledDaysText,
22873             format : this.useIso ? 'Y-m-d' : this.format,
22874             minText : String.format(this.minText, this.formatDate(this.minValue)),
22875             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22876         });
22877         this.menu.on(Roo.apply({}, this.menuListeners, {
22878             scope:this
22879         }));
22880         this.menu.picker.setValue(this.getValue() || new Date());
22881         this.menu.show(this.el, "tl-bl?");
22882     },
22883
22884     beforeBlur : function(){
22885         var v = this.parseDate(this.getRawValue());
22886         if(v){
22887             this.setValue(v);
22888         }
22889     }
22890
22891     /** @cfg {Boolean} grow @hide */
22892     /** @cfg {Number} growMin @hide */
22893     /** @cfg {Number} growMax @hide */
22894     /**
22895      * @hide
22896      * @method autoSize
22897      */
22898 });/*
22899  * Based on:
22900  * Ext JS Library 1.1.1
22901  * Copyright(c) 2006-2007, Ext JS, LLC.
22902  *
22903  * Originally Released Under LGPL - original licence link has changed is not relivant.
22904  *
22905  * Fork - LGPL
22906  * <script type="text/javascript">
22907  */
22908  
22909 /**
22910  * @class Roo.form.MonthField
22911  * @extends Roo.form.TriggerField
22912  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22913 * @constructor
22914 * Create a new MonthField
22915 * @param {Object} config
22916  */
22917 Roo.form.MonthField = function(config){
22918     
22919     Roo.form.MonthField.superclass.constructor.call(this, config);
22920     
22921       this.addEvents({
22922          
22923         /**
22924          * @event select
22925          * Fires when a date is selected
22926              * @param {Roo.form.MonthFieeld} combo This combo box
22927              * @param {Date} date The date selected
22928              */
22929         'select' : true
22930          
22931     });
22932     
22933     
22934     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22935     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22936     this.ddMatch = null;
22937     if(this.disabledDates){
22938         var dd = this.disabledDates;
22939         var re = "(?:";
22940         for(var i = 0; i < dd.length; i++){
22941             re += dd[i];
22942             if(i != dd.length-1) re += "|";
22943         }
22944         this.ddMatch = new RegExp(re + ")");
22945     }
22946 };
22947
22948 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
22949     /**
22950      * @cfg {String} format
22951      * The default date format string which can be overriden for localization support.  The format must be
22952      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22953      */
22954     format : "M Y",
22955     /**
22956      * @cfg {String} altFormats
22957      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22958      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22959      */
22960     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
22961     /**
22962      * @cfg {Array} disabledDays
22963      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22964      */
22965     disabledDays : [0,1,2,3,4,5,6],
22966     /**
22967      * @cfg {String} disabledDaysText
22968      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22969      */
22970     disabledDaysText : "Disabled",
22971     /**
22972      * @cfg {Array} disabledDates
22973      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22974      * expression so they are very powerful. Some examples:
22975      * <ul>
22976      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22977      * <li>["03/08", "09/16"] would disable those days for every year</li>
22978      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22979      * <li>["03/../2006"] would disable every day in March 2006</li>
22980      * <li>["^03"] would disable every day in every March</li>
22981      * </ul>
22982      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22983      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22984      */
22985     disabledDates : null,
22986     /**
22987      * @cfg {String} disabledDatesText
22988      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22989      */
22990     disabledDatesText : "Disabled",
22991     /**
22992      * @cfg {Date/String} minValue
22993      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22994      * valid format (defaults to null).
22995      */
22996     minValue : null,
22997     /**
22998      * @cfg {Date/String} maxValue
22999      * The maximum allowed date. Can be either a Javascript date object or a string date in a
23000      * valid format (defaults to null).
23001      */
23002     maxValue : null,
23003     /**
23004      * @cfg {String} minText
23005      * The error text to display when the date in the cell is before minValue (defaults to
23006      * 'The date in this field must be after {minValue}').
23007      */
23008     minText : "The date in this field must be equal to or after {0}",
23009     /**
23010      * @cfg {String} maxTextf
23011      * The error text to display when the date in the cell is after maxValue (defaults to
23012      * 'The date in this field must be before {maxValue}').
23013      */
23014     maxText : "The date in this field must be equal to or before {0}",
23015     /**
23016      * @cfg {String} invalidText
23017      * The error text to display when the date in the field is invalid (defaults to
23018      * '{value} is not a valid date - it must be in the format {format}').
23019      */
23020     invalidText : "{0} is not a valid date - it must be in the format {1}",
23021     /**
23022      * @cfg {String} triggerClass
23023      * An additional CSS class used to style the trigger button.  The trigger will always get the
23024      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
23025      * which displays a calendar icon).
23026      */
23027     triggerClass : 'x-form-date-trigger',
23028     
23029
23030     /**
23031      * @cfg {Boolean} useIso
23032      * if enabled, then the date field will use a hidden field to store the 
23033      * real value as iso formated date. default (true)
23034      */ 
23035     useIso : true,
23036     /**
23037      * @cfg {String/Object} autoCreate
23038      * A DomHelper element spec, or true for a default element spec (defaults to
23039      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
23040      */ 
23041     // private
23042     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
23043     
23044     // private
23045     hiddenField: false,
23046     
23047     hideMonthPicker : false,
23048     
23049     onRender : function(ct, position)
23050     {
23051         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
23052         if (this.useIso) {
23053             this.el.dom.removeAttribute('name'); 
23054             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
23055                     'before', true);
23056             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
23057             // prevent input submission
23058             this.hiddenName = this.name;
23059         }
23060             
23061             
23062     },
23063     
23064     // private
23065     validateValue : function(value)
23066     {
23067         value = this.formatDate(value);
23068         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
23069             return false;
23070         }
23071         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
23072              return true;
23073         }
23074         var svalue = value;
23075         value = this.parseDate(value);
23076         if(!value){
23077             this.markInvalid(String.format(this.invalidText, svalue, this.format));
23078             return false;
23079         }
23080         var time = value.getTime();
23081         if(this.minValue && time < this.minValue.getTime()){
23082             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
23083             return false;
23084         }
23085         if(this.maxValue && time > this.maxValue.getTime()){
23086             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
23087             return false;
23088         }
23089         /*if(this.disabledDays){
23090             var day = value.getDay();
23091             for(var i = 0; i < this.disabledDays.length; i++) {
23092                 if(day === this.disabledDays[i]){
23093                     this.markInvalid(this.disabledDaysText);
23094                     return false;
23095                 }
23096             }
23097         }
23098         */
23099         var fvalue = this.formatDate(value);
23100         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
23101             this.markInvalid(String.format(this.disabledDatesText, fvalue));
23102             return false;
23103         }
23104         */
23105         return true;
23106     },
23107
23108     // private
23109     // Provides logic to override the default TriggerField.validateBlur which just returns true
23110     validateBlur : function(){
23111         return !this.menu || !this.menu.isVisible();
23112     },
23113
23114     /**
23115      * Returns the current date value of the date field.
23116      * @return {Date} The date value
23117      */
23118     getValue : function(){
23119         
23120         
23121         
23122         return  this.hiddenField ?
23123                 this.hiddenField.value :
23124                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
23125     },
23126
23127     /**
23128      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
23129      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
23130      * (the default format used is "m/d/y").
23131      * <br />Usage:
23132      * <pre><code>
23133 //All of these calls set the same date value (May 4, 2006)
23134
23135 //Pass a date object:
23136 var dt = new Date('5/4/06');
23137 monthField.setValue(dt);
23138
23139 //Pass a date string (default format):
23140 monthField.setValue('5/4/06');
23141
23142 //Pass a date string (custom format):
23143 monthField.format = 'Y-m-d';
23144 monthField.setValue('2006-5-4');
23145 </code></pre>
23146      * @param {String/Date} date The date or valid date string
23147      */
23148     setValue : function(date){
23149         Roo.log('month setValue' + date);
23150         if (this.hiddenField) {
23151             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
23152         }
23153         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
23154         this.value = this.parseDate(date);
23155     },
23156
23157     // private
23158     parseDate : function(value){
23159         if(!value || value instanceof Date){
23160             return value;
23161         }
23162         var v = Date.parseDate(value, this.format);
23163         if (this.useIso) {
23164             v = Date.parseDate(value, 'Y-m-d');
23165         }
23166         
23167         
23168         if(!v && this.altFormats){
23169             if(!this.altFormatsArray){
23170                 this.altFormatsArray = this.altFormats.split("|");
23171             }
23172             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23173                 v = Date.parseDate(value, this.altFormatsArray[i]);
23174             }
23175         }
23176         return v;
23177     },
23178
23179     // private
23180     formatDate : function(date, fmt){
23181         return (!date || !(date instanceof Date)) ?
23182                date : date.dateFormat(fmt || this.format);
23183     },
23184
23185     // private
23186     menuListeners : {
23187         select: function(m, d){
23188             this.setValue(d);
23189             this.fireEvent('select', this, d);
23190         },
23191         show : function(){ // retain focus styling
23192             this.onFocus();
23193         },
23194         hide : function(){
23195             this.focus.defer(10, this);
23196             var ml = this.menuListeners;
23197             this.menu.un("select", ml.select,  this);
23198             this.menu.un("show", ml.show,  this);
23199             this.menu.un("hide", ml.hide,  this);
23200         }
23201     },
23202     // private
23203     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
23204     onTriggerClick : function(){
23205         if(this.disabled){
23206             return;
23207         }
23208         if(this.menu == null){
23209             this.menu = new Roo.menu.DateMenu();
23210            
23211         }
23212         
23213         Roo.apply(this.menu.picker,  {
23214             
23215             showClear: this.allowBlank,
23216             minDate : this.minValue,
23217             maxDate : this.maxValue,
23218             disabledDatesRE : this.ddMatch,
23219             disabledDatesText : this.disabledDatesText,
23220             
23221             format : this.useIso ? 'Y-m-d' : this.format,
23222             minText : String.format(this.minText, this.formatDate(this.minValue)),
23223             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
23224             
23225         });
23226          this.menu.on(Roo.apply({}, this.menuListeners, {
23227             scope:this
23228         }));
23229        
23230         
23231         var m = this.menu;
23232         var p = m.picker;
23233         
23234         
23235         p.hideMonthPicker  = function(disableAnim){
23236             if(this.monthPicker){
23237                 Roo.log("hideMonthPicker called");
23238                 if(disableAnim === true){
23239                     this.monthPicker.hide();
23240                 }else{
23241                     this.monthPicker.slideOut('t', {duration:.2});
23242                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth));
23243                     p.fireEvent("select", this, this.value);
23244                     m.hide();
23245                 }
23246             }
23247         }
23248         
23249         Roo.log('picker set value');
23250         Roo.log(this.getValue());
23251         p.setValue(this.getValue() || new Date());
23252         m.show(this.el, 'tl-bl?');
23253         
23254         // hidden the day picker
23255         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
23256         
23257         
23258         
23259       
23260         
23261         p.showMonthPicker.defer(100, p);
23262     
23263         
23264        
23265     },
23266
23267     beforeBlur : function(){
23268         var v = this.parseDate(this.getRawValue());
23269         if(v){
23270             this.setValue(v);
23271         }
23272     }
23273
23274     /** @cfg {Boolean} grow @hide */
23275     /** @cfg {Number} growMin @hide */
23276     /** @cfg {Number} growMax @hide */
23277     /**
23278      * @hide
23279      * @method autoSize
23280      */
23281 });/*
23282  * Based on:
23283  * Ext JS Library 1.1.1
23284  * Copyright(c) 2006-2007, Ext JS, LLC.
23285  *
23286  * Originally Released Under LGPL - original licence link has changed is not relivant.
23287  *
23288  * Fork - LGPL
23289  * <script type="text/javascript">
23290  */
23291  
23292
23293 /**
23294  * @class Roo.form.ComboBox
23295  * @extends Roo.form.TriggerField
23296  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
23297  * @constructor
23298  * Create a new ComboBox.
23299  * @param {Object} config Configuration options
23300  */
23301 Roo.form.ComboBox = function(config){
23302     Roo.form.ComboBox.superclass.constructor.call(this, config);
23303     this.addEvents({
23304         /**
23305          * @event expand
23306          * Fires when the dropdown list is expanded
23307              * @param {Roo.form.ComboBox} combo This combo box
23308              */
23309         'expand' : true,
23310         /**
23311          * @event collapse
23312          * Fires when the dropdown list is collapsed
23313              * @param {Roo.form.ComboBox} combo This combo box
23314              */
23315         'collapse' : true,
23316         /**
23317          * @event beforeselect
23318          * Fires before a list item is selected. Return false to cancel the selection.
23319              * @param {Roo.form.ComboBox} combo This combo box
23320              * @param {Roo.data.Record} record The data record returned from the underlying store
23321              * @param {Number} index The index of the selected item in the dropdown list
23322              */
23323         'beforeselect' : true,
23324         /**
23325          * @event select
23326          * Fires when a list item is selected
23327              * @param {Roo.form.ComboBox} combo This combo box
23328              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
23329              * @param {Number} index The index of the selected item in the dropdown list
23330              */
23331         'select' : true,
23332         /**
23333          * @event beforequery
23334          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
23335          * The event object passed has these properties:
23336              * @param {Roo.form.ComboBox} combo This combo box
23337              * @param {String} query The query
23338              * @param {Boolean} forceAll true to force "all" query
23339              * @param {Boolean} cancel true to cancel the query
23340              * @param {Object} e The query event object
23341              */
23342         'beforequery': true,
23343          /**
23344          * @event add
23345          * Fires when the 'add' icon is pressed (add a listener to enable add button)
23346              * @param {Roo.form.ComboBox} combo This combo box
23347              */
23348         'add' : true,
23349         /**
23350          * @event edit
23351          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
23352              * @param {Roo.form.ComboBox} combo This combo box
23353              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
23354              */
23355         'edit' : true
23356         
23357         
23358     });
23359     if(this.transform){
23360         this.allowDomMove = false;
23361         var s = Roo.getDom(this.transform);
23362         if(!this.hiddenName){
23363             this.hiddenName = s.name;
23364         }
23365         if(!this.store){
23366             this.mode = 'local';
23367             var d = [], opts = s.options;
23368             for(var i = 0, len = opts.length;i < len; i++){
23369                 var o = opts[i];
23370                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
23371                 if(o.selected) {
23372                     this.value = value;
23373                 }
23374                 d.push([value, o.text]);
23375             }
23376             this.store = new Roo.data.SimpleStore({
23377                 'id': 0,
23378                 fields: ['value', 'text'],
23379                 data : d
23380             });
23381             this.valueField = 'value';
23382             this.displayField = 'text';
23383         }
23384         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
23385         if(!this.lazyRender){
23386             this.target = true;
23387             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
23388             s.parentNode.removeChild(s); // remove it
23389             this.render(this.el.parentNode);
23390         }else{
23391             s.parentNode.removeChild(s); // remove it
23392         }
23393
23394     }
23395     if (this.store) {
23396         this.store = Roo.factory(this.store, Roo.data);
23397     }
23398     
23399     this.selectedIndex = -1;
23400     if(this.mode == 'local'){
23401         if(config.queryDelay === undefined){
23402             this.queryDelay = 10;
23403         }
23404         if(config.minChars === undefined){
23405             this.minChars = 0;
23406         }
23407     }
23408 };
23409
23410 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
23411     /**
23412      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
23413      */
23414     /**
23415      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
23416      * rendering into an Roo.Editor, defaults to false)
23417      */
23418     /**
23419      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
23420      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
23421      */
23422     /**
23423      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
23424      */
23425     /**
23426      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
23427      * the dropdown list (defaults to undefined, with no header element)
23428      */
23429
23430      /**
23431      * @cfg {String/Roo.Template} tpl The template to use to render the output
23432      */
23433      
23434     // private
23435     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
23436     /**
23437      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
23438      */
23439     listWidth: undefined,
23440     /**
23441      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
23442      * mode = 'remote' or 'text' if mode = 'local')
23443      */
23444     displayField: undefined,
23445     /**
23446      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
23447      * mode = 'remote' or 'value' if mode = 'local'). 
23448      * Note: use of a valueField requires the user make a selection
23449      * in order for a value to be mapped.
23450      */
23451     valueField: undefined,
23452     
23453     
23454     /**
23455      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
23456      * field's data value (defaults to the underlying DOM element's name)
23457      */
23458     hiddenName: undefined,
23459     /**
23460      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
23461      */
23462     listClass: '',
23463     /**
23464      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
23465      */
23466     selectedClass: 'x-combo-selected',
23467     /**
23468      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
23469      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
23470      * which displays a downward arrow icon).
23471      */
23472     triggerClass : 'x-form-arrow-trigger',
23473     /**
23474      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
23475      */
23476     shadow:'sides',
23477     /**
23478      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
23479      * anchor positions (defaults to 'tl-bl')
23480      */
23481     listAlign: 'tl-bl?',
23482     /**
23483      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
23484      */
23485     maxHeight: 300,
23486     /**
23487      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
23488      * query specified by the allQuery config option (defaults to 'query')
23489      */
23490     triggerAction: 'query',
23491     /**
23492      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
23493      * (defaults to 4, does not apply if editable = false)
23494      */
23495     minChars : 4,
23496     /**
23497      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
23498      * delay (typeAheadDelay) if it matches a known value (defaults to false)
23499      */
23500     typeAhead: false,
23501     /**
23502      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
23503      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
23504      */
23505     queryDelay: 500,
23506     /**
23507      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
23508      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
23509      */
23510     pageSize: 0,
23511     /**
23512      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
23513      * when editable = true (defaults to false)
23514      */
23515     selectOnFocus:false,
23516     /**
23517      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
23518      */
23519     queryParam: 'query',
23520     /**
23521      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
23522      * when mode = 'remote' (defaults to 'Loading...')
23523      */
23524     loadingText: 'Loading...',
23525     /**
23526      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
23527      */
23528     resizable: false,
23529     /**
23530      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
23531      */
23532     handleHeight : 8,
23533     /**
23534      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
23535      * traditional select (defaults to true)
23536      */
23537     editable: true,
23538     /**
23539      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
23540      */
23541     allQuery: '',
23542     /**
23543      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
23544      */
23545     mode: 'remote',
23546     /**
23547      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
23548      * listWidth has a higher value)
23549      */
23550     minListWidth : 70,
23551     /**
23552      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
23553      * allow the user to set arbitrary text into the field (defaults to false)
23554      */
23555     forceSelection:false,
23556     /**
23557      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
23558      * if typeAhead = true (defaults to 250)
23559      */
23560     typeAheadDelay : 250,
23561     /**
23562      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
23563      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
23564      */
23565     valueNotFoundText : undefined,
23566     /**
23567      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
23568      */
23569     blockFocus : false,
23570     
23571     /**
23572      * @cfg {Boolean} disableClear Disable showing of clear button.
23573      */
23574     disableClear : false,
23575     /**
23576      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
23577      */
23578     alwaysQuery : false,
23579     
23580     //private
23581     addicon : false,
23582     editicon: false,
23583     
23584     // element that contains real text value.. (when hidden is used..)
23585      
23586     // private
23587     onRender : function(ct, position){
23588         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
23589         if(this.hiddenName){
23590             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
23591                     'before', true);
23592             this.hiddenField.value =
23593                 this.hiddenValue !== undefined ? this.hiddenValue :
23594                 this.value !== undefined ? this.value : '';
23595
23596             // prevent input submission
23597             this.el.dom.removeAttribute('name');
23598              
23599              
23600         }
23601         if(Roo.isGecko){
23602             this.el.dom.setAttribute('autocomplete', 'off');
23603         }
23604
23605         var cls = 'x-combo-list';
23606
23607         this.list = new Roo.Layer({
23608             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
23609         });
23610
23611         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
23612         this.list.setWidth(lw);
23613         this.list.swallowEvent('mousewheel');
23614         this.assetHeight = 0;
23615
23616         if(this.title){
23617             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
23618             this.assetHeight += this.header.getHeight();
23619         }
23620
23621         this.innerList = this.list.createChild({cls:cls+'-inner'});
23622         this.innerList.on('mouseover', this.onViewOver, this);
23623         this.innerList.on('mousemove', this.onViewMove, this);
23624         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23625         
23626         if(this.allowBlank && !this.pageSize && !this.disableClear){
23627             this.footer = this.list.createChild({cls:cls+'-ft'});
23628             this.pageTb = new Roo.Toolbar(this.footer);
23629            
23630         }
23631         if(this.pageSize){
23632             this.footer = this.list.createChild({cls:cls+'-ft'});
23633             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
23634                     {pageSize: this.pageSize});
23635             
23636         }
23637         
23638         if (this.pageTb && this.allowBlank && !this.disableClear) {
23639             var _this = this;
23640             this.pageTb.add(new Roo.Toolbar.Fill(), {
23641                 cls: 'x-btn-icon x-btn-clear',
23642                 text: '&#160;',
23643                 handler: function()
23644                 {
23645                     _this.collapse();
23646                     _this.clearValue();
23647                     _this.onSelect(false, -1);
23648                 }
23649             });
23650         }
23651         if (this.footer) {
23652             this.assetHeight += this.footer.getHeight();
23653         }
23654         
23655
23656         if(!this.tpl){
23657             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
23658         }
23659
23660         this.view = new Roo.View(this.innerList, this.tpl, {
23661             singleSelect:true, store: this.store, selectedClass: this.selectedClass
23662         });
23663
23664         this.view.on('click', this.onViewClick, this);
23665
23666         this.store.on('beforeload', this.onBeforeLoad, this);
23667         this.store.on('load', this.onLoad, this);
23668         this.store.on('loadexception', this.onLoadException, this);
23669
23670         if(this.resizable){
23671             this.resizer = new Roo.Resizable(this.list,  {
23672                pinned:true, handles:'se'
23673             });
23674             this.resizer.on('resize', function(r, w, h){
23675                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23676                 this.listWidth = w;
23677                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23678                 this.restrictHeight();
23679             }, this);
23680             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23681         }
23682         if(!this.editable){
23683             this.editable = true;
23684             this.setEditable(false);
23685         }  
23686         
23687         
23688         if (typeof(this.events.add.listeners) != 'undefined') {
23689             
23690             this.addicon = this.wrap.createChild(
23691                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
23692        
23693             this.addicon.on('click', function(e) {
23694                 this.fireEvent('add', this);
23695             }, this);
23696         }
23697         if (typeof(this.events.edit.listeners) != 'undefined') {
23698             
23699             this.editicon = this.wrap.createChild(
23700                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
23701             if (this.addicon) {
23702                 this.editicon.setStyle('margin-left', '40px');
23703             }
23704             this.editicon.on('click', function(e) {
23705                 
23706                 // we fire even  if inothing is selected..
23707                 this.fireEvent('edit', this, this.lastData );
23708                 
23709             }, this);
23710         }
23711         
23712         
23713         
23714     },
23715
23716     // private
23717     initEvents : function(){
23718         Roo.form.ComboBox.superclass.initEvents.call(this);
23719
23720         this.keyNav = new Roo.KeyNav(this.el, {
23721             "up" : function(e){
23722                 this.inKeyMode = true;
23723                 this.selectPrev();
23724             },
23725
23726             "down" : function(e){
23727                 if(!this.isExpanded()){
23728                     this.onTriggerClick();
23729                 }else{
23730                     this.inKeyMode = true;
23731                     this.selectNext();
23732                 }
23733             },
23734
23735             "enter" : function(e){
23736                 this.onViewClick();
23737                 //return true;
23738             },
23739
23740             "esc" : function(e){
23741                 this.collapse();
23742             },
23743
23744             "tab" : function(e){
23745                 this.onViewClick(false);
23746                 this.fireEvent("specialkey", this, e);
23747                 return true;
23748             },
23749
23750             scope : this,
23751
23752             doRelay : function(foo, bar, hname){
23753                 if(hname == 'down' || this.scope.isExpanded()){
23754                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23755                 }
23756                 return true;
23757             },
23758
23759             forceKeyDown: true
23760         });
23761         this.queryDelay = Math.max(this.queryDelay || 10,
23762                 this.mode == 'local' ? 10 : 250);
23763         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23764         if(this.typeAhead){
23765             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23766         }
23767         if(this.editable !== false){
23768             this.el.on("keyup", this.onKeyUp, this);
23769         }
23770         if(this.forceSelection){
23771             this.on('blur', this.doForce, this);
23772         }
23773     },
23774
23775     onDestroy : function(){
23776         if(this.view){
23777             this.view.setStore(null);
23778             this.view.el.removeAllListeners();
23779             this.view.el.remove();
23780             this.view.purgeListeners();
23781         }
23782         if(this.list){
23783             this.list.destroy();
23784         }
23785         if(this.store){
23786             this.store.un('beforeload', this.onBeforeLoad, this);
23787             this.store.un('load', this.onLoad, this);
23788             this.store.un('loadexception', this.onLoadException, this);
23789         }
23790         Roo.form.ComboBox.superclass.onDestroy.call(this);
23791     },
23792
23793     // private
23794     fireKey : function(e){
23795         if(e.isNavKeyPress() && !this.list.isVisible()){
23796             this.fireEvent("specialkey", this, e);
23797         }
23798     },
23799
23800     // private
23801     onResize: function(w, h){
23802         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23803         
23804         if(typeof w != 'number'){
23805             // we do not handle it!?!?
23806             return;
23807         }
23808         var tw = this.trigger.getWidth();
23809         tw += this.addicon ? this.addicon.getWidth() : 0;
23810         tw += this.editicon ? this.editicon.getWidth() : 0;
23811         var x = w - tw;
23812         this.el.setWidth( this.adjustWidth('input', x));
23813             
23814         this.trigger.setStyle('left', x+'px');
23815         
23816         if(this.list && this.listWidth === undefined){
23817             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23818             this.list.setWidth(lw);
23819             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23820         }
23821         
23822     
23823         
23824     },
23825
23826     /**
23827      * Allow or prevent the user from directly editing the field text.  If false is passed,
23828      * the user will only be able to select from the items defined in the dropdown list.  This method
23829      * is the runtime equivalent of setting the 'editable' config option at config time.
23830      * @param {Boolean} value True to allow the user to directly edit the field text
23831      */
23832     setEditable : function(value){
23833         if(value == this.editable){
23834             return;
23835         }
23836         this.editable = value;
23837         if(!value){
23838             this.el.dom.setAttribute('readOnly', true);
23839             this.el.on('mousedown', this.onTriggerClick,  this);
23840             this.el.addClass('x-combo-noedit');
23841         }else{
23842             this.el.dom.setAttribute('readOnly', false);
23843             this.el.un('mousedown', this.onTriggerClick,  this);
23844             this.el.removeClass('x-combo-noedit');
23845         }
23846     },
23847
23848     // private
23849     onBeforeLoad : function(){
23850         if(!this.hasFocus){
23851             return;
23852         }
23853         this.innerList.update(this.loadingText ?
23854                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23855         this.restrictHeight();
23856         this.selectedIndex = -1;
23857     },
23858
23859     // private
23860     onLoad : function(){
23861         if(!this.hasFocus){
23862             return;
23863         }
23864         if(this.store.getCount() > 0){
23865             this.expand();
23866             this.restrictHeight();
23867             if(this.lastQuery == this.allQuery){
23868                 if(this.editable){
23869                     this.el.dom.select();
23870                 }
23871                 if(!this.selectByValue(this.value, true)){
23872                     this.select(0, true);
23873                 }
23874             }else{
23875                 this.selectNext();
23876                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23877                     this.taTask.delay(this.typeAheadDelay);
23878                 }
23879             }
23880         }else{
23881             this.onEmptyResults();
23882         }
23883         //this.el.focus();
23884     },
23885     // private
23886     onLoadException : function()
23887     {
23888         this.collapse();
23889         Roo.log(this.store.reader.jsonData);
23890         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
23891             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
23892         }
23893         
23894         
23895     },
23896     // private
23897     onTypeAhead : function(){
23898         if(this.store.getCount() > 0){
23899             var r = this.store.getAt(0);
23900             var newValue = r.data[this.displayField];
23901             var len = newValue.length;
23902             var selStart = this.getRawValue().length;
23903             if(selStart != len){
23904                 this.setRawValue(newValue);
23905                 this.selectText(selStart, newValue.length);
23906             }
23907         }
23908     },
23909
23910     // private
23911     onSelect : function(record, index){
23912         if(this.fireEvent('beforeselect', this, record, index) !== false){
23913             this.setFromData(index > -1 ? record.data : false);
23914             this.collapse();
23915             this.fireEvent('select', this, record, index);
23916         }
23917     },
23918
23919     /**
23920      * Returns the currently selected field value or empty string if no value is set.
23921      * @return {String} value The selected value
23922      */
23923     getValue : function(){
23924         if(this.valueField){
23925             return typeof this.value != 'undefined' ? this.value : '';
23926         }else{
23927             return Roo.form.ComboBox.superclass.getValue.call(this);
23928         }
23929     },
23930
23931     /**
23932      * Clears any text/value currently set in the field
23933      */
23934     clearValue : function(){
23935         if(this.hiddenField){
23936             this.hiddenField.value = '';
23937         }
23938         this.value = '';
23939         this.setRawValue('');
23940         this.lastSelectionText = '';
23941         this.applyEmptyText();
23942     },
23943
23944     /**
23945      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23946      * will be displayed in the field.  If the value does not match the data value of an existing item,
23947      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23948      * Otherwise the field will be blank (although the value will still be set).
23949      * @param {String} value The value to match
23950      */
23951     setValue : function(v){
23952         var text = v;
23953         if(this.valueField){
23954             var r = this.findRecord(this.valueField, v);
23955             if(r){
23956                 text = r.data[this.displayField];
23957             }else if(this.valueNotFoundText !== undefined){
23958                 text = this.valueNotFoundText;
23959             }
23960         }
23961         this.lastSelectionText = text;
23962         if(this.hiddenField){
23963             this.hiddenField.value = v;
23964         }
23965         Roo.form.ComboBox.superclass.setValue.call(this, text);
23966         this.value = v;
23967     },
23968     /**
23969      * @property {Object} the last set data for the element
23970      */
23971     
23972     lastData : false,
23973     /**
23974      * Sets the value of the field based on a object which is related to the record format for the store.
23975      * @param {Object} value the value to set as. or false on reset?
23976      */
23977     setFromData : function(o){
23978         var dv = ''; // display value
23979         var vv = ''; // value value..
23980         this.lastData = o;
23981         if (this.displayField) {
23982             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23983         } else {
23984             // this is an error condition!!!
23985             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23986         }
23987         
23988         if(this.valueField){
23989             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23990         }
23991         if(this.hiddenField){
23992             this.hiddenField.value = vv;
23993             
23994             this.lastSelectionText = dv;
23995             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23996             this.value = vv;
23997             return;
23998         }
23999         // no hidden field.. - we store the value in 'value', but still display
24000         // display field!!!!
24001         this.lastSelectionText = dv;
24002         Roo.form.ComboBox.superclass.setValue.call(this, dv);
24003         this.value = vv;
24004         
24005         
24006     },
24007     // private
24008     reset : function(){
24009         // overridden so that last data is reset..
24010         this.setValue(this.originalValue);
24011         this.clearInvalid();
24012         this.lastData = false;
24013     },
24014     // private
24015     findRecord : function(prop, value){
24016         var record;
24017         if(this.store.getCount() > 0){
24018             this.store.each(function(r){
24019                 if(r.data[prop] == value){
24020                     record = r;
24021                     return false;
24022                 }
24023                 return true;
24024             });
24025         }
24026         return record;
24027     },
24028     
24029     getName: function()
24030     {
24031         // returns hidden if it's set..
24032         if (!this.rendered) {return ''};
24033         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
24034         
24035     },
24036     // private
24037     onViewMove : function(e, t){
24038         this.inKeyMode = false;
24039     },
24040
24041     // private
24042     onViewOver : function(e, t){
24043         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
24044             return;
24045         }
24046         var item = this.view.findItemFromChild(t);
24047         if(item){
24048             var index = this.view.indexOf(item);
24049             this.select(index, false);
24050         }
24051     },
24052
24053     // private
24054     onViewClick : function(doFocus)
24055     {
24056         var index = this.view.getSelectedIndexes()[0];
24057         var r = this.store.getAt(index);
24058         if(r){
24059             this.onSelect(r, index);
24060         }
24061         if(doFocus !== false && !this.blockFocus){
24062             this.el.focus();
24063         }
24064     },
24065
24066     // private
24067     restrictHeight : function(){
24068         this.innerList.dom.style.height = '';
24069         var inner = this.innerList.dom;
24070         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
24071         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
24072         this.list.beginUpdate();
24073         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
24074         this.list.alignTo(this.el, this.listAlign);
24075         this.list.endUpdate();
24076     },
24077
24078     // private
24079     onEmptyResults : function(){
24080         this.collapse();
24081     },
24082
24083     /**
24084      * Returns true if the dropdown list is expanded, else false.
24085      */
24086     isExpanded : function(){
24087         return this.list.isVisible();
24088     },
24089
24090     /**
24091      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
24092      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
24093      * @param {String} value The data value of the item to select
24094      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
24095      * selected item if it is not currently in view (defaults to true)
24096      * @return {Boolean} True if the value matched an item in the list, else false
24097      */
24098     selectByValue : function(v, scrollIntoView){
24099         if(v !== undefined && v !== null){
24100             var r = this.findRecord(this.valueField || this.displayField, v);
24101             if(r){
24102                 this.select(this.store.indexOf(r), scrollIntoView);
24103                 return true;
24104             }
24105         }
24106         return false;
24107     },
24108
24109     /**
24110      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
24111      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
24112      * @param {Number} index The zero-based index of the list item to select
24113      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
24114      * selected item if it is not currently in view (defaults to true)
24115      */
24116     select : function(index, scrollIntoView){
24117         this.selectedIndex = index;
24118         this.view.select(index);
24119         if(scrollIntoView !== false){
24120             var el = this.view.getNode(index);
24121             if(el){
24122                 this.innerList.scrollChildIntoView(el, false);
24123             }
24124         }
24125     },
24126
24127     // private
24128     selectNext : function(){
24129         var ct = this.store.getCount();
24130         if(ct > 0){
24131             if(this.selectedIndex == -1){
24132                 this.select(0);
24133             }else if(this.selectedIndex < ct-1){
24134                 this.select(this.selectedIndex+1);
24135             }
24136         }
24137     },
24138
24139     // private
24140     selectPrev : function(){
24141         var ct = this.store.getCount();
24142         if(ct > 0){
24143             if(this.selectedIndex == -1){
24144                 this.select(0);
24145             }else if(this.selectedIndex != 0){
24146                 this.select(this.selectedIndex-1);
24147             }
24148         }
24149     },
24150
24151     // private
24152     onKeyUp : function(e){
24153         if(this.editable !== false && !e.isSpecialKey()){
24154             this.lastKey = e.getKey();
24155             this.dqTask.delay(this.queryDelay);
24156         }
24157     },
24158
24159     // private
24160     validateBlur : function(){
24161         return !this.list || !this.list.isVisible();   
24162     },
24163
24164     // private
24165     initQuery : function(){
24166         this.doQuery(this.getRawValue());
24167     },
24168
24169     // private
24170     doForce : function(){
24171         if(this.el.dom.value.length > 0){
24172             this.el.dom.value =
24173                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
24174             this.applyEmptyText();
24175         }
24176     },
24177
24178     /**
24179      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
24180      * query allowing the query action to be canceled if needed.
24181      * @param {String} query The SQL query to execute
24182      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
24183      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
24184      * saved in the current store (defaults to false)
24185      */
24186     doQuery : function(q, forceAll){
24187         if(q === undefined || q === null){
24188             q = '';
24189         }
24190         var qe = {
24191             query: q,
24192             forceAll: forceAll,
24193             combo: this,
24194             cancel:false
24195         };
24196         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
24197             return false;
24198         }
24199         q = qe.query;
24200         forceAll = qe.forceAll;
24201         if(forceAll === true || (q.length >= this.minChars)){
24202             if(this.lastQuery != q || this.alwaysQuery){
24203                 this.lastQuery = q;
24204                 if(this.mode == 'local'){
24205                     this.selectedIndex = -1;
24206                     if(forceAll){
24207                         this.store.clearFilter();
24208                     }else{
24209                         this.store.filter(this.displayField, q);
24210                     }
24211                     this.onLoad();
24212                 }else{
24213                     this.store.baseParams[this.queryParam] = q;
24214                     this.store.load({
24215                         params: this.getParams(q)
24216                     });
24217                     this.expand();
24218                 }
24219             }else{
24220                 this.selectedIndex = -1;
24221                 this.onLoad();   
24222             }
24223         }
24224     },
24225
24226     // private
24227     getParams : function(q){
24228         var p = {};
24229         //p[this.queryParam] = q;
24230         if(this.pageSize){
24231             p.start = 0;
24232             p.limit = this.pageSize;
24233         }
24234         return p;
24235     },
24236
24237     /**
24238      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
24239      */
24240     collapse : function(){
24241         if(!this.isExpanded()){
24242             return;
24243         }
24244         this.list.hide();
24245         Roo.get(document).un('mousedown', this.collapseIf, this);
24246         Roo.get(document).un('mousewheel', this.collapseIf, this);
24247         if (!this.editable) {
24248             Roo.get(document).un('keydown', this.listKeyPress, this);
24249         }
24250         this.fireEvent('collapse', this);
24251     },
24252
24253     // private
24254     collapseIf : function(e){
24255         if(!e.within(this.wrap) && !e.within(this.list)){
24256             this.collapse();
24257         }
24258     },
24259
24260     /**
24261      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
24262      */
24263     expand : function(){
24264         if(this.isExpanded() || !this.hasFocus){
24265             return;
24266         }
24267         this.list.alignTo(this.el, this.listAlign);
24268         this.list.show();
24269         Roo.get(document).on('mousedown', this.collapseIf, this);
24270         Roo.get(document).on('mousewheel', this.collapseIf, this);
24271         if (!this.editable) {
24272             Roo.get(document).on('keydown', this.listKeyPress, this);
24273         }
24274         
24275         this.fireEvent('expand', this);
24276     },
24277
24278     // private
24279     // Implements the default empty TriggerField.onTriggerClick function
24280     onTriggerClick : function(){
24281         if(this.disabled){
24282             return;
24283         }
24284         if(this.isExpanded()){
24285             this.collapse();
24286             if (!this.blockFocus) {
24287                 this.el.focus();
24288             }
24289             
24290         }else {
24291             this.hasFocus = true;
24292             if(this.triggerAction == 'all') {
24293                 this.doQuery(this.allQuery, true);
24294             } else {
24295                 this.doQuery(this.getRawValue());
24296             }
24297             if (!this.blockFocus) {
24298                 this.el.focus();
24299             }
24300         }
24301     },
24302     listKeyPress : function(e)
24303     {
24304         //Roo.log('listkeypress');
24305         // scroll to first matching element based on key pres..
24306         if (e.isSpecialKey()) {
24307             return false;
24308         }
24309         var k = String.fromCharCode(e.getKey()).toUpperCase();
24310         //Roo.log(k);
24311         var match  = false;
24312         var csel = this.view.getSelectedNodes();
24313         var cselitem = false;
24314         if (csel.length) {
24315             var ix = this.view.indexOf(csel[0]);
24316             cselitem  = this.store.getAt(ix);
24317             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
24318                 cselitem = false;
24319             }
24320             
24321         }
24322         
24323         this.store.each(function(v) { 
24324             if (cselitem) {
24325                 // start at existing selection.
24326                 if (cselitem.id == v.id) {
24327                     cselitem = false;
24328                 }
24329                 return;
24330             }
24331                 
24332             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
24333                 match = this.store.indexOf(v);
24334                 return false;
24335             }
24336         }, this);
24337         
24338         if (match === false) {
24339             return true; // no more action?
24340         }
24341         // scroll to?
24342         this.view.select(match);
24343         var sn = Roo.get(this.view.getSelectedNodes()[0])
24344         sn.scrollIntoView(sn.dom.parentNode, false);
24345     }
24346
24347     /** 
24348     * @cfg {Boolean} grow 
24349     * @hide 
24350     */
24351     /** 
24352     * @cfg {Number} growMin 
24353     * @hide 
24354     */
24355     /** 
24356     * @cfg {Number} growMax 
24357     * @hide 
24358     */
24359     /**
24360      * @hide
24361      * @method autoSize
24362      */
24363 });/*
24364  * Copyright(c) 2010-2012, Roo J Solutions Limited
24365  *
24366  * Licence LGPL
24367  *
24368  */
24369
24370 /**
24371  * @class Roo.form.ComboBoxArray
24372  * @extends Roo.form.TextField
24373  * A facebook style adder... for lists of email / people / countries  etc...
24374  * pick multiple items from a combo box, and shows each one.
24375  *
24376  *  Fred [x]  Brian [x]  [Pick another |v]
24377  *
24378  *
24379  *  For this to work: it needs various extra information
24380  *    - normal combo problay has
24381  *      name, hiddenName
24382  *    + displayField, valueField
24383  *
24384  *    For our purpose...
24385  *
24386  *
24387  *   If we change from 'extends' to wrapping...
24388  *   
24389  *  
24390  *
24391  
24392  
24393  * @constructor
24394  * Create a new ComboBoxArray.
24395  * @param {Object} config Configuration options
24396  */
24397  
24398
24399 Roo.form.ComboBoxArray = function(config)
24400 {
24401     
24402     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
24403     
24404     this.items = new Roo.util.MixedCollection(false);
24405     
24406     // construct the child combo...
24407     
24408     
24409     
24410     
24411    
24412     
24413 }
24414
24415  
24416 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
24417
24418     /**
24419      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
24420      */
24421     
24422     lastData : false,
24423     
24424     // behavies liek a hiddne field
24425     inputType:      'hidden',
24426     /**
24427      * @cfg {Number} width The width of the box that displays the selected element
24428      */ 
24429     width:          300,
24430
24431     
24432     
24433     /**
24434      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
24435      */
24436     name : false,
24437     /**
24438      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
24439      */
24440     hiddenName : false,
24441     
24442     
24443     // private the array of items that are displayed..
24444     items  : false,
24445     // private - the hidden field el.
24446     hiddenEl : false,
24447     // private - the filed el..
24448     el : false,
24449     
24450     //validateValue : function() { return true; }, // all values are ok!
24451     //onAddClick: function() { },
24452     
24453     onRender : function(ct, position) 
24454     {
24455         
24456         // create the standard hidden element
24457         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
24458         
24459         
24460         // give fake names to child combo;
24461         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
24462         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
24463         
24464         this.combo = Roo.factory(this.combo, Roo.form);
24465         this.combo.onRender(ct, position);
24466         
24467         // assigned so form know we need to do this..
24468         this.store          = this.combo.store;
24469         this.valueField     = this.combo.valueField;
24470         this.displayField   = this.combo.displayField ;
24471         
24472         
24473         this.combo.wrap.addClass('x-cbarray-grp');
24474         
24475         var cbwrap = this.combo.wrap.createChild(
24476             {tag: 'div', cls: 'x-cbarray-cb'},
24477             this.combo.el.dom
24478         );
24479         
24480              
24481         this.hiddenEl = this.combo.wrap.createChild({
24482             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
24483         });
24484         this.el = this.combo.wrap.createChild({
24485             tag: 'input',  type:'hidden' , name: this.name, value : ''
24486         });
24487          //   this.el.dom.removeAttribute("name");
24488         
24489         
24490         this.outerWrap = this.combo.wrap;
24491         this.wrap = cbwrap;
24492         
24493         this.outerWrap.setWidth(this.width);
24494         this.outerWrap.dom.removeChild(this.el.dom);
24495         
24496         this.wrap.dom.appendChild(this.el.dom);
24497         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
24498         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
24499         
24500         this.combo.trigger.setStyle('position','relative');
24501         this.combo.trigger.setStyle('left', '0px');
24502         this.combo.trigger.setStyle('top', '2px');
24503         
24504         this.combo.el.setStyle('vertical-align', 'text-bottom');
24505         
24506         //this.trigger.setStyle('vertical-align', 'top');
24507         
24508         // this should use the code from combo really... on('add' ....)
24509         if (this.adder) {
24510             
24511         
24512             this.adder = this.outerWrap.createChild(
24513                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
24514             var _t = this;
24515             this.adder.on('click', function(e) {
24516                 _t.fireEvent('adderclick', this, e);
24517             }, _t);
24518         }
24519         //var _t = this;
24520         //this.adder.on('click', this.onAddClick, _t);
24521         
24522         
24523         this.combo.on('select', function(cb, rec, ix) {
24524             this.addItem(rec.data);
24525             
24526             cb.setValue('');
24527             cb.el.dom.value = '';
24528             //cb.lastData = rec.data;
24529             // add to list
24530             
24531         }, this);
24532         
24533         
24534     },
24535     
24536     
24537     getName: function()
24538     {
24539         // returns hidden if it's set..
24540         if (!this.rendered) {return ''};
24541         return  this.hiddenName ? this.hiddenName : this.name;
24542         
24543     },
24544     
24545     
24546     onResize: function(w, h){
24547         
24548         return;
24549         // not sure if this is needed..
24550         //this.combo.onResize(w,h);
24551         
24552         if(typeof w != 'number'){
24553             // we do not handle it!?!?
24554             return;
24555         }
24556         var tw = this.combo.trigger.getWidth();
24557         tw += this.addicon ? this.addicon.getWidth() : 0;
24558         tw += this.editicon ? this.editicon.getWidth() : 0;
24559         var x = w - tw;
24560         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
24561             
24562         this.combo.trigger.setStyle('left', '0px');
24563         
24564         if(this.list && this.listWidth === undefined){
24565             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
24566             this.list.setWidth(lw);
24567             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
24568         }
24569         
24570     
24571         
24572     },
24573     
24574     addItem: function(rec)
24575     {
24576         var valueField = this.combo.valueField;
24577         var displayField = this.combo.displayField;
24578         if (this.items.indexOfKey(rec[valueField]) > -1) {
24579             //console.log("GOT " + rec.data.id);
24580             return;
24581         }
24582         
24583         var x = new Roo.form.ComboBoxArray.Item({
24584             //id : rec[this.idField],
24585             data : rec,
24586             displayField : displayField ,
24587             tipField : displayField ,
24588             cb : this
24589         });
24590         // use the 
24591         this.items.add(rec[valueField],x);
24592         // add it before the element..
24593         this.updateHiddenEl();
24594         x.render(this.outerWrap, this.wrap.dom);
24595         // add the image handler..
24596     },
24597     
24598     updateHiddenEl : function()
24599     {
24600         this.validate();
24601         if (!this.hiddenEl) {
24602             return;
24603         }
24604         var ar = [];
24605         var idField = this.combo.valueField;
24606         
24607         this.items.each(function(f) {
24608             ar.push(f.data[idField]);
24609            
24610         });
24611         this.hiddenEl.dom.value = ar.join(',');
24612         this.validate();
24613     },
24614     
24615     reset : function()
24616     {
24617         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
24618         this.items.each(function(f) {
24619            f.remove(); 
24620         });
24621         this.el.dom.value = '';
24622         if (this.hiddenEl) {
24623             this.hiddenEl.dom.value = '';
24624         }
24625         
24626     },
24627     getValue: function()
24628     {
24629         return this.hiddenEl ? this.hiddenEl.dom.value : '';
24630     },
24631     setValue: function(v) // not a valid action - must use addItems..
24632     {
24633          
24634         this.reset();
24635         
24636         
24637         
24638         if (this.store.isLocal && (typeof(v) == 'string')) {
24639             // then we can use the store to find the values..
24640             // comma seperated at present.. this needs to allow JSON based encoding..
24641             this.hiddenEl.value  = v;
24642             var v_ar = [];
24643             Roo.each(v.split(','), function(k) {
24644                 Roo.log("CHECK " + this.valueField + ',' + k);
24645                 var li = this.store.query(this.valueField, k);
24646                 if (!li.length) {
24647                     return;
24648                 }
24649                 add = {};
24650                 add[this.valueField] = k;
24651                 add[this.displayField] = li.item(0).data[this.displayField];
24652                 
24653                 this.addItem(add);
24654             }, this) 
24655              
24656         }
24657         if (typeof(v) == 'object') {
24658             // then let's assume it's an array of objects..
24659             Roo.each(v, function(l) {
24660                 this.addItem(l);
24661             }, this);
24662              
24663         }
24664         
24665         
24666     },
24667     setFromData: function(v)
24668     {
24669         // this recieves an object, if setValues is called.
24670         this.reset();
24671         this.el.dom.value = v[this.displayField];
24672         this.hiddenEl.dom.value = v[this.valueField];
24673         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
24674             return;
24675         }
24676         var kv = v[this.valueField];
24677         var dv = v[this.displayField];
24678         kv = typeof(kv) != 'string' ? '' : kv;
24679         dv = typeof(dv) != 'string' ? '' : dv;
24680         
24681         
24682         var keys = kv.split(',');
24683         var display = dv.split(',');
24684         for (var i = 0 ; i < keys.length; i++) {
24685             
24686             add = {};
24687             add[this.valueField] = keys[i];
24688             add[this.displayField] = display[i];
24689             this.addItem(add);
24690         }
24691       
24692         
24693     },
24694     
24695     
24696     validateValue : function(value){
24697         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
24698         
24699     }
24700     
24701 });
24702
24703
24704
24705 /**
24706  * @class Roo.form.ComboBoxArray.Item
24707  * @extends Roo.BoxComponent
24708  * A selected item in the list
24709  *  Fred [x]  Brian [x]  [Pick another |v]
24710  * 
24711  * @constructor
24712  * Create a new item.
24713  * @param {Object} config Configuration options
24714  */
24715  
24716 Roo.form.ComboBoxArray.Item = function(config) {
24717     config.id = Roo.id();
24718     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
24719 }
24720
24721 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
24722     data : {},
24723     cb: false,
24724     displayField : false,
24725     tipField : false,
24726     
24727     
24728     defaultAutoCreate : {
24729         tag: 'div',
24730         cls: 'x-cbarray-item',
24731         cn : [ 
24732             { tag: 'div' },
24733             {
24734                 tag: 'img',
24735                 width:16,
24736                 height : 16,
24737                 src : Roo.BLANK_IMAGE_URL ,
24738                 align: 'center'
24739             }
24740         ]
24741         
24742     },
24743     
24744  
24745     onRender : function(ct, position)
24746     {
24747         Roo.form.Field.superclass.onRender.call(this, ct, position);
24748         
24749         if(!this.el){
24750             var cfg = this.getAutoCreate();
24751             this.el = ct.createChild(cfg, position);
24752         }
24753         
24754         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
24755         
24756         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
24757             this.cb.renderer(this.data) :
24758             String.format('{0}',this.data[this.displayField]);
24759         
24760             
24761         this.el.child('div').dom.setAttribute('qtip',
24762                         String.format('{0}',this.data[this.tipField])
24763         );
24764         
24765         this.el.child('img').on('click', this.remove, this);
24766         
24767     },
24768    
24769     remove : function()
24770     {
24771         
24772         this.cb.items.remove(this);
24773         this.el.child('img').un('click', this.remove, this);
24774         this.el.remove();
24775         this.cb.updateHiddenEl();
24776     }
24777     
24778     
24779 });/*
24780  * Based on:
24781  * Ext JS Library 1.1.1
24782  * Copyright(c) 2006-2007, Ext JS, LLC.
24783  *
24784  * Originally Released Under LGPL - original licence link has changed is not relivant.
24785  *
24786  * Fork - LGPL
24787  * <script type="text/javascript">
24788  */
24789 /**
24790  * @class Roo.form.Checkbox
24791  * @extends Roo.form.Field
24792  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
24793  * @constructor
24794  * Creates a new Checkbox
24795  * @param {Object} config Configuration options
24796  */
24797 Roo.form.Checkbox = function(config){
24798     Roo.form.Checkbox.superclass.constructor.call(this, config);
24799     this.addEvents({
24800         /**
24801          * @event check
24802          * Fires when the checkbox is checked or unchecked.
24803              * @param {Roo.form.Checkbox} this This checkbox
24804              * @param {Boolean} checked The new checked value
24805              */
24806         check : true
24807     });
24808 };
24809
24810 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
24811     /**
24812      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
24813      */
24814     focusClass : undefined,
24815     /**
24816      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
24817      */
24818     fieldClass: "x-form-field",
24819     /**
24820      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
24821      */
24822     checked: false,
24823     /**
24824      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
24825      * {tag: "input", type: "checkbox", autocomplete: "off"})
24826      */
24827     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
24828     /**
24829      * @cfg {String} boxLabel The text that appears beside the checkbox
24830      */
24831     boxLabel : "",
24832     /**
24833      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
24834      */  
24835     inputValue : '1',
24836     /**
24837      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24838      */
24839      valueOff: '0', // value when not checked..
24840
24841     actionMode : 'viewEl', 
24842     //
24843     // private
24844     itemCls : 'x-menu-check-item x-form-item',
24845     groupClass : 'x-menu-group-item',
24846     inputType : 'hidden',
24847     
24848     
24849     inSetChecked: false, // check that we are not calling self...
24850     
24851     inputElement: false, // real input element?
24852     basedOn: false, // ????
24853     
24854     isFormField: true, // not sure where this is needed!!!!
24855
24856     onResize : function(){
24857         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
24858         if(!this.boxLabel){
24859             this.el.alignTo(this.wrap, 'c-c');
24860         }
24861     },
24862
24863     initEvents : function(){
24864         Roo.form.Checkbox.superclass.initEvents.call(this);
24865         this.el.on("click", this.onClick,  this);
24866         this.el.on("change", this.onClick,  this);
24867     },
24868
24869
24870     getResizeEl : function(){
24871         return this.wrap;
24872     },
24873
24874     getPositionEl : function(){
24875         return this.wrap;
24876     },
24877
24878     // private
24879     onRender : function(ct, position){
24880         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24881         /*
24882         if(this.inputValue !== undefined){
24883             this.el.dom.value = this.inputValue;
24884         }
24885         */
24886         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24887         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24888         var viewEl = this.wrap.createChild({ 
24889             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24890         this.viewEl = viewEl;   
24891         this.wrap.on('click', this.onClick,  this); 
24892         
24893         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
24894         this.el.on('propertychange', this.setFromHidden,  this);  //ie
24895         
24896         
24897         
24898         if(this.boxLabel){
24899             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24900         //    viewEl.on('click', this.onClick,  this); 
24901         }
24902         //if(this.checked){
24903             this.setChecked(this.checked);
24904         //}else{
24905             //this.checked = this.el.dom;
24906         //}
24907
24908     },
24909
24910     // private
24911     initValue : Roo.emptyFn,
24912
24913     /**
24914      * Returns the checked state of the checkbox.
24915      * @return {Boolean} True if checked, else false
24916      */
24917     getValue : function(){
24918         if(this.el){
24919             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
24920         }
24921         return this.valueOff;
24922         
24923     },
24924
24925         // private
24926     onClick : function(){ 
24927         this.setChecked(!this.checked);
24928
24929         //if(this.el.dom.checked != this.checked){
24930         //    this.setValue(this.el.dom.checked);
24931        // }
24932     },
24933
24934     /**
24935      * Sets the checked state of the checkbox.
24936      * On is always based on a string comparison between inputValue and the param.
24937      * @param {Boolean/String} value - the value to set 
24938      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
24939      */
24940     setValue : function(v,suppressEvent){
24941         
24942         
24943         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
24944         //if(this.el && this.el.dom){
24945         //    this.el.dom.checked = this.checked;
24946         //    this.el.dom.defaultChecked = this.checked;
24947         //}
24948         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
24949         //this.fireEvent("check", this, this.checked);
24950     },
24951     // private..
24952     setChecked : function(state,suppressEvent)
24953     {
24954         if (this.inSetChecked) {
24955             this.checked = state;
24956             return;
24957         }
24958         
24959     
24960         if(this.wrap){
24961             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
24962         }
24963         this.checked = state;
24964         if(suppressEvent !== true){
24965             this.fireEvent('check', this, state);
24966         }
24967         this.inSetChecked = true;
24968         this.el.dom.value = state ? this.inputValue : this.valueOff;
24969         this.inSetChecked = false;
24970         
24971     },
24972     // handle setting of hidden value by some other method!!?!?
24973     setFromHidden: function()
24974     {
24975         if(!this.el){
24976             return;
24977         }
24978         //console.log("SET FROM HIDDEN");
24979         //alert('setFrom hidden');
24980         this.setValue(this.el.dom.value);
24981     },
24982     
24983     onDestroy : function()
24984     {
24985         if(this.viewEl){
24986             Roo.get(this.viewEl).remove();
24987         }
24988          
24989         Roo.form.Checkbox.superclass.onDestroy.call(this);
24990     }
24991
24992 });/*
24993  * Based on:
24994  * Ext JS Library 1.1.1
24995  * Copyright(c) 2006-2007, Ext JS, LLC.
24996  *
24997  * Originally Released Under LGPL - original licence link has changed is not relivant.
24998  *
24999  * Fork - LGPL
25000  * <script type="text/javascript">
25001  */
25002  
25003 /**
25004  * @class Roo.form.Radio
25005  * @extends Roo.form.Checkbox
25006  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
25007  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
25008  * @constructor
25009  * Creates a new Radio
25010  * @param {Object} config Configuration options
25011  */
25012 Roo.form.Radio = function(){
25013     Roo.form.Radio.superclass.constructor.apply(this, arguments);
25014 };
25015 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
25016     inputType: 'radio',
25017
25018     /**
25019      * If this radio is part of a group, it will return the selected value
25020      * @return {String}
25021      */
25022     getGroupValue : function(){
25023         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
25024     }
25025 });//<script type="text/javascript">
25026
25027 /*
25028  * Ext JS Library 1.1.1
25029  * Copyright(c) 2006-2007, Ext JS, LLC.
25030  * licensing@extjs.com
25031  * 
25032  * http://www.extjs.com/license
25033  */
25034  
25035  /*
25036   * 
25037   * Known bugs:
25038   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
25039   * - IE ? - no idea how much works there.
25040   * 
25041   * 
25042   * 
25043   */
25044  
25045
25046 /**
25047  * @class Ext.form.HtmlEditor
25048  * @extends Ext.form.Field
25049  * Provides a lightweight HTML Editor component.
25050  *
25051  * This has been tested on Fireforx / Chrome.. IE may not be so great..
25052  * 
25053  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
25054  * supported by this editor.</b><br/><br/>
25055  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
25056  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25057  */
25058 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
25059       /**
25060      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25061      */
25062     toolbars : false,
25063     /**
25064      * @cfg {String} createLinkText The default text for the create link prompt
25065      */
25066     createLinkText : 'Please enter the URL for the link:',
25067     /**
25068      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
25069      */
25070     defaultLinkValue : 'http:/'+'/',
25071    
25072      /**
25073      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25074      *                        Roo.resizable.
25075      */
25076     resizable : false,
25077      /**
25078      * @cfg {Number} height (in pixels)
25079      */   
25080     height: 300,
25081    /**
25082      * @cfg {Number} width (in pixels)
25083      */   
25084     width: 500,
25085     
25086     /**
25087      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25088      * 
25089      */
25090     stylesheets: false,
25091     
25092     // id of frame..
25093     frameId: false,
25094     
25095     // private properties
25096     validationEvent : false,
25097     deferHeight: true,
25098     initialized : false,
25099     activated : false,
25100     sourceEditMode : false,
25101     onFocus : Roo.emptyFn,
25102     iframePad:3,
25103     hideMode:'offsets',
25104     
25105     defaultAutoCreate : { // modified by initCompnoent..
25106         tag: "textarea",
25107         style:"width:500px;height:300px;",
25108         autocomplete: "off"
25109     },
25110
25111     // private
25112     initComponent : function(){
25113         this.addEvents({
25114             /**
25115              * @event initialize
25116              * Fires when the editor is fully initialized (including the iframe)
25117              * @param {HtmlEditor} this
25118              */
25119             initialize: true,
25120             /**
25121              * @event activate
25122              * Fires when the editor is first receives the focus. Any insertion must wait
25123              * until after this event.
25124              * @param {HtmlEditor} this
25125              */
25126             activate: true,
25127              /**
25128              * @event beforesync
25129              * Fires before the textarea is updated with content from the editor iframe. Return false
25130              * to cancel the sync.
25131              * @param {HtmlEditor} this
25132              * @param {String} html
25133              */
25134             beforesync: true,
25135              /**
25136              * @event beforepush
25137              * Fires before the iframe editor is updated with content from the textarea. Return false
25138              * to cancel the push.
25139              * @param {HtmlEditor} this
25140              * @param {String} html
25141              */
25142             beforepush: true,
25143              /**
25144              * @event sync
25145              * Fires when the textarea is updated with content from the editor iframe.
25146              * @param {HtmlEditor} this
25147              * @param {String} html
25148              */
25149             sync: true,
25150              /**
25151              * @event push
25152              * Fires when the iframe editor is updated with content from the textarea.
25153              * @param {HtmlEditor} this
25154              * @param {String} html
25155              */
25156             push: true,
25157              /**
25158              * @event editmodechange
25159              * Fires when the editor switches edit modes
25160              * @param {HtmlEditor} this
25161              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25162              */
25163             editmodechange: true,
25164             /**
25165              * @event editorevent
25166              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25167              * @param {HtmlEditor} this
25168              */
25169             editorevent: true
25170         });
25171         this.defaultAutoCreate =  {
25172             tag: "textarea",
25173             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
25174             autocomplete: "off"
25175         };
25176     },
25177
25178     /**
25179      * Protected method that will not generally be called directly. It
25180      * is called when the editor creates its toolbar. Override this method if you need to
25181      * add custom toolbar buttons.
25182      * @param {HtmlEditor} editor
25183      */
25184     createToolbar : function(editor){
25185         if (!editor.toolbars || !editor.toolbars.length) {
25186             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
25187         }
25188         
25189         for (var i =0 ; i < editor.toolbars.length;i++) {
25190             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
25191             editor.toolbars[i].init(editor);
25192         }
25193          
25194         
25195     },
25196
25197     /**
25198      * Protected method that will not generally be called directly. It
25199      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25200      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25201      */
25202     getDocMarkup : function(){
25203         // body styles..
25204         var st = '';
25205         if (this.stylesheets === false) {
25206             
25207             Roo.get(document.head).select('style').each(function(node) {
25208                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25209             });
25210             
25211             Roo.get(document.head).select('link').each(function(node) { 
25212                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25213             });
25214             
25215         } else if (!this.stylesheets.length) {
25216                 // simple..
25217                 st = '<style type="text/css">' +
25218                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25219                    '</style>';
25220         } else {
25221             Roo.each(this.stylesheets, function(s) {
25222                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
25223             });
25224             
25225         }
25226         
25227         st +=  '<style type="text/css">' +
25228             'IMG { cursor: pointer } ' +
25229         '</style>';
25230
25231         
25232         return '<html><head>' + st  +
25233             //<style type="text/css">' +
25234             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25235             //'</style>' +
25236             ' </head><body class="roo-htmleditor-body"></body></html>';
25237     },
25238
25239     // private
25240     onRender : function(ct, position)
25241     {
25242         var _t = this;
25243         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
25244         this.el.dom.style.border = '0 none';
25245         this.el.dom.setAttribute('tabIndex', -1);
25246         this.el.addClass('x-hidden');
25247         if(Roo.isIE){ // fix IE 1px bogus margin
25248             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25249         }
25250         this.wrap = this.el.wrap({
25251             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25252         });
25253         
25254         if (this.resizable) {
25255             this.resizeEl = new Roo.Resizable(this.wrap, {
25256                 pinned : true,
25257                 wrap: true,
25258                 dynamic : true,
25259                 minHeight : this.height,
25260                 height: this.height,
25261                 handles : this.resizable,
25262                 width: this.width,
25263                 listeners : {
25264                     resize : function(r, w, h) {
25265                         _t.onResize(w,h); // -something
25266                     }
25267                 }
25268             });
25269             
25270         }
25271
25272         this.frameId = Roo.id();
25273         
25274         this.createToolbar(this);
25275         
25276       
25277         
25278         var iframe = this.wrap.createChild({
25279             tag: 'iframe',
25280             id: this.frameId,
25281             name: this.frameId,
25282             frameBorder : 'no',
25283             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25284         }, this.el
25285         );
25286         
25287        // console.log(iframe);
25288         //this.wrap.dom.appendChild(iframe);
25289
25290         this.iframe = iframe.dom;
25291
25292          this.assignDocWin();
25293         
25294         this.doc.designMode = 'on';
25295        
25296         this.doc.open();
25297         this.doc.write(this.getDocMarkup());
25298         this.doc.close();
25299
25300         
25301         var task = { // must defer to wait for browser to be ready
25302             run : function(){
25303                 //console.log("run task?" + this.doc.readyState);
25304                 this.assignDocWin();
25305                 if(this.doc.body || this.doc.readyState == 'complete'){
25306                     try {
25307                         this.doc.designMode="on";
25308                     } catch (e) {
25309                         return;
25310                     }
25311                     Roo.TaskMgr.stop(task);
25312                     this.initEditor.defer(10, this);
25313                 }
25314             },
25315             interval : 10,
25316             duration:10000,
25317             scope: this
25318         };
25319         Roo.TaskMgr.start(task);
25320
25321         if(!this.width){
25322             this.setSize(this.wrap.getSize());
25323         }
25324         if (this.resizeEl) {
25325             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25326             // should trigger onReize..
25327         }
25328     },
25329
25330     // private
25331     onResize : function(w, h)
25332     {
25333         //Roo.log('resize: ' +w + ',' + h );
25334         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
25335         if(this.el && this.iframe){
25336             if(typeof w == 'number'){
25337                 var aw = w - this.wrap.getFrameWidth('lr');
25338                 this.el.setWidth(this.adjustWidth('textarea', aw));
25339                 this.iframe.style.width = aw + 'px';
25340             }
25341             if(typeof h == 'number'){
25342                 var tbh = 0;
25343                 for (var i =0; i < this.toolbars.length;i++) {
25344                     // fixme - ask toolbars for heights?
25345                     tbh += this.toolbars[i].tb.el.getHeight();
25346                     if (this.toolbars[i].footer) {
25347                         tbh += this.toolbars[i].footer.el.getHeight();
25348                     }
25349                 }
25350                 
25351                 
25352                 
25353                 
25354                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25355                 ah -= 5; // knock a few pixes off for look..
25356                 this.el.setHeight(this.adjustWidth('textarea', ah));
25357                 this.iframe.style.height = ah + 'px';
25358                 if(this.doc){
25359                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
25360                 }
25361             }
25362         }
25363     },
25364
25365     /**
25366      * Toggles the editor between standard and source edit mode.
25367      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25368      */
25369     toggleSourceEdit : function(sourceEditMode){
25370         
25371         this.sourceEditMode = sourceEditMode === true;
25372         
25373         if(this.sourceEditMode){
25374           
25375             this.syncValue();
25376             this.iframe.className = 'x-hidden';
25377             this.el.removeClass('x-hidden');
25378             this.el.dom.removeAttribute('tabIndex');
25379             this.el.focus();
25380         }else{
25381              
25382             this.pushValue();
25383             this.iframe.className = '';
25384             this.el.addClass('x-hidden');
25385             this.el.dom.setAttribute('tabIndex', -1);
25386             this.deferFocus();
25387         }
25388         this.setSize(this.wrap.getSize());
25389         this.fireEvent('editmodechange', this, this.sourceEditMode);
25390     },
25391
25392     // private used internally
25393     createLink : function(){
25394         var url = prompt(this.createLinkText, this.defaultLinkValue);
25395         if(url && url != 'http:/'+'/'){
25396             this.relayCmd('createlink', url);
25397         }
25398     },
25399
25400     // private (for BoxComponent)
25401     adjustSize : Roo.BoxComponent.prototype.adjustSize,
25402
25403     // private (for BoxComponent)
25404     getResizeEl : function(){
25405         return this.wrap;
25406     },
25407
25408     // private (for BoxComponent)
25409     getPositionEl : function(){
25410         return this.wrap;
25411     },
25412
25413     // private
25414     initEvents : function(){
25415         this.originalValue = this.getValue();
25416     },
25417
25418     /**
25419      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25420      * @method
25421      */
25422     markInvalid : Roo.emptyFn,
25423     /**
25424      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25425      * @method
25426      */
25427     clearInvalid : Roo.emptyFn,
25428
25429     setValue : function(v){
25430         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
25431         this.pushValue();
25432     },
25433
25434     /**
25435      * Protected method that will not generally be called directly. If you need/want
25436      * custom HTML cleanup, this is the method you should override.
25437      * @param {String} html The HTML to be cleaned
25438      * return {String} The cleaned HTML
25439      */
25440     cleanHtml : function(html){
25441         html = String(html);
25442         if(html.length > 5){
25443             if(Roo.isSafari){ // strip safari nonsense
25444                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25445             }
25446         }
25447         if(html == '&nbsp;'){
25448             html = '';
25449         }
25450         return html;
25451     },
25452
25453     /**
25454      * Protected method that will not generally be called directly. Syncs the contents
25455      * of the editor iframe with the textarea.
25456      */
25457     syncValue : function(){
25458         if(this.initialized){
25459             var bd = (this.doc.body || this.doc.documentElement);
25460             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25461             var html = bd.innerHTML;
25462             if(Roo.isSafari){
25463                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25464                 var m = bs.match(/text-align:(.*?);/i);
25465                 if(m && m[1]){
25466                     html = '<div style="'+m[0]+'">' + html + '</div>';
25467                 }
25468             }
25469             html = this.cleanHtml(html);
25470             // fix up the special chars..
25471             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
25472                 return "&#"+b.charCodeAt()+";" 
25473             });
25474             if(this.fireEvent('beforesync', this, html) !== false){
25475                 this.el.dom.value = html;
25476                 this.fireEvent('sync', this, html);
25477             }
25478         }
25479     },
25480
25481     /**
25482      * Protected method that will not generally be called directly. Pushes the value of the textarea
25483      * into the iframe editor.
25484      */
25485     pushValue : function(){
25486         if(this.initialized){
25487             var v = this.el.dom.value;
25488             if(v.length < 1){
25489                 v = '&#160;';
25490             }
25491             
25492             if(this.fireEvent('beforepush', this, v) !== false){
25493                 var d = (this.doc.body || this.doc.documentElement);
25494                 d.innerHTML = v;
25495                 this.cleanUpPaste();
25496                 this.el.dom.value = d.innerHTML;
25497                 this.fireEvent('push', this, v);
25498             }
25499         }
25500     },
25501
25502     // private
25503     deferFocus : function(){
25504         this.focus.defer(10, this);
25505     },
25506
25507     // doc'ed in Field
25508     focus : function(){
25509         if(this.win && !this.sourceEditMode){
25510             this.win.focus();
25511         }else{
25512             this.el.focus();
25513         }
25514     },
25515     
25516     assignDocWin: function()
25517     {
25518         var iframe = this.iframe;
25519         
25520          if(Roo.isIE){
25521             this.doc = iframe.contentWindow.document;
25522             this.win = iframe.contentWindow;
25523         } else {
25524             if (!Roo.get(this.frameId)) {
25525                 return;
25526             }
25527             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25528             this.win = Roo.get(this.frameId).dom.contentWindow;
25529         }
25530     },
25531     
25532     // private
25533     initEditor : function(){
25534         //console.log("INIT EDITOR");
25535         this.assignDocWin();
25536         
25537         
25538         
25539         this.doc.designMode="on";
25540         this.doc.open();
25541         this.doc.write(this.getDocMarkup());
25542         this.doc.close();
25543         
25544         var dbody = (this.doc.body || this.doc.documentElement);
25545         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25546         // this copies styles from the containing element into thsi one..
25547         // not sure why we need all of this..
25548         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25549         ss['background-attachment'] = 'fixed'; // w3c
25550         dbody.bgProperties = 'fixed'; // ie
25551         Roo.DomHelper.applyStyles(dbody, ss);
25552         Roo.EventManager.on(this.doc, {
25553             //'mousedown': this.onEditorEvent,
25554             'mouseup': this.onEditorEvent,
25555             'dblclick': this.onEditorEvent,
25556             'click': this.onEditorEvent,
25557             'keyup': this.onEditorEvent,
25558             buffer:100,
25559             scope: this
25560         });
25561         if(Roo.isGecko){
25562             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25563         }
25564         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25565             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25566         }
25567         this.initialized = true;
25568
25569         this.fireEvent('initialize', this);
25570         this.pushValue();
25571     },
25572
25573     // private
25574     onDestroy : function(){
25575         
25576         
25577         
25578         if(this.rendered){
25579             
25580             for (var i =0; i < this.toolbars.length;i++) {
25581                 // fixme - ask toolbars for heights?
25582                 this.toolbars[i].onDestroy();
25583             }
25584             
25585             this.wrap.dom.innerHTML = '';
25586             this.wrap.remove();
25587         }
25588     },
25589
25590     // private
25591     onFirstFocus : function(){
25592         
25593         this.assignDocWin();
25594         
25595         
25596         this.activated = true;
25597         for (var i =0; i < this.toolbars.length;i++) {
25598             this.toolbars[i].onFirstFocus();
25599         }
25600        
25601         if(Roo.isGecko){ // prevent silly gecko errors
25602             this.win.focus();
25603             var s = this.win.getSelection();
25604             if(!s.focusNode || s.focusNode.nodeType != 3){
25605                 var r = s.getRangeAt(0);
25606                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25607                 r.collapse(true);
25608                 this.deferFocus();
25609             }
25610             try{
25611                 this.execCmd('useCSS', true);
25612                 this.execCmd('styleWithCSS', false);
25613             }catch(e){}
25614         }
25615         this.fireEvent('activate', this);
25616     },
25617
25618     // private
25619     adjustFont: function(btn){
25620         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25621         //if(Roo.isSafari){ // safari
25622         //    adjust *= 2;
25623        // }
25624         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25625         if(Roo.isSafari){ // safari
25626             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25627             v =  (v < 10) ? 10 : v;
25628             v =  (v > 48) ? 48 : v;
25629             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25630             
25631         }
25632         
25633         
25634         v = Math.max(1, v+adjust);
25635         
25636         this.execCmd('FontSize', v  );
25637     },
25638
25639     onEditorEvent : function(e){
25640         this.fireEvent('editorevent', this, e);
25641       //  this.updateToolbar();
25642         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25643     },
25644
25645     insertTag : function(tg)
25646     {
25647         // could be a bit smarter... -> wrap the current selected tRoo..
25648         
25649         this.execCmd("formatblock",   tg);
25650         
25651     },
25652     
25653     insertText : function(txt)
25654     {
25655         
25656         
25657         range = this.createRange();
25658         range.deleteContents();
25659                //alert(Sender.getAttribute('label'));
25660                
25661         range.insertNode(this.doc.createTextNode(txt));
25662     } ,
25663     
25664     // private
25665     relayBtnCmd : function(btn){
25666         this.relayCmd(btn.cmd);
25667     },
25668
25669     /**
25670      * Executes a Midas editor command on the editor document and performs necessary focus and
25671      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25672      * @param {String} cmd The Midas command
25673      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25674      */
25675     relayCmd : function(cmd, value){
25676         this.win.focus();
25677         this.execCmd(cmd, value);
25678         this.fireEvent('editorevent', this);
25679         //this.updateToolbar();
25680         this.deferFocus();
25681     },
25682
25683     /**
25684      * Executes a Midas editor command directly on the editor document.
25685      * For visual commands, you should use {@link #relayCmd} instead.
25686      * <b>This should only be called after the editor is initialized.</b>
25687      * @param {String} cmd The Midas command
25688      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25689      */
25690     execCmd : function(cmd, value){
25691         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25692         this.syncValue();
25693     },
25694  
25695  
25696    
25697     /**
25698      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25699      * to insert tRoo.
25700      * @param {String} text | dom node.. 
25701      */
25702     insertAtCursor : function(text)
25703     {
25704         
25705         
25706         
25707         if(!this.activated){
25708             return;
25709         }
25710         /*
25711         if(Roo.isIE){
25712             this.win.focus();
25713             var r = this.doc.selection.createRange();
25714             if(r){
25715                 r.collapse(true);
25716                 r.pasteHTML(text);
25717                 this.syncValue();
25718                 this.deferFocus();
25719             
25720             }
25721             return;
25722         }
25723         */
25724         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25725             this.win.focus();
25726             
25727             
25728             // from jquery ui (MIT licenced)
25729             var range, node;
25730             var win = this.win;
25731             
25732             if (win.getSelection && win.getSelection().getRangeAt) {
25733                 range = win.getSelection().getRangeAt(0);
25734                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25735                 range.insertNode(node);
25736             } else if (win.document.selection && win.document.selection.createRange) {
25737                 // no firefox support
25738                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25739                 win.document.selection.createRange().pasteHTML(txt);
25740             } else {
25741                 // no firefox support
25742                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25743                 this.execCmd('InsertHTML', txt);
25744             } 
25745             
25746             this.syncValue();
25747             
25748             this.deferFocus();
25749         }
25750     },
25751  // private
25752     mozKeyPress : function(e){
25753         if(e.ctrlKey){
25754             var c = e.getCharCode(), cmd;
25755           
25756             if(c > 0){
25757                 c = String.fromCharCode(c).toLowerCase();
25758                 switch(c){
25759                     case 'b':
25760                         cmd = 'bold';
25761                         break;
25762                     case 'i':
25763                         cmd = 'italic';
25764                         break;
25765                     
25766                     case 'u':
25767                         cmd = 'underline';
25768                         break;
25769                     
25770                     case 'v':
25771                         this.cleanUpPaste.defer(100, this);
25772                         return;
25773                         
25774                 }
25775                 if(cmd){
25776                     this.win.focus();
25777                     this.execCmd(cmd);
25778                     this.deferFocus();
25779                     e.preventDefault();
25780                 }
25781                 
25782             }
25783         }
25784     },
25785
25786     // private
25787     fixKeys : function(){ // load time branching for fastest keydown performance
25788         if(Roo.isIE){
25789             return function(e){
25790                 var k = e.getKey(), r;
25791                 if(k == e.TAB){
25792                     e.stopEvent();
25793                     r = this.doc.selection.createRange();
25794                     if(r){
25795                         r.collapse(true);
25796                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25797                         this.deferFocus();
25798                     }
25799                     return;
25800                 }
25801                 
25802                 if(k == e.ENTER){
25803                     r = this.doc.selection.createRange();
25804                     if(r){
25805                         var target = r.parentElement();
25806                         if(!target || target.tagName.toLowerCase() != 'li'){
25807                             e.stopEvent();
25808                             r.pasteHTML('<br />');
25809                             r.collapse(false);
25810                             r.select();
25811                         }
25812                     }
25813                 }
25814                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25815                     this.cleanUpPaste.defer(100, this);
25816                     return;
25817                 }
25818                 
25819                 
25820             };
25821         }else if(Roo.isOpera){
25822             return function(e){
25823                 var k = e.getKey();
25824                 if(k == e.TAB){
25825                     e.stopEvent();
25826                     this.win.focus();
25827                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25828                     this.deferFocus();
25829                 }
25830                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25831                     this.cleanUpPaste.defer(100, this);
25832                     return;
25833                 }
25834                 
25835             };
25836         }else if(Roo.isSafari){
25837             return function(e){
25838                 var k = e.getKey();
25839                 
25840                 if(k == e.TAB){
25841                     e.stopEvent();
25842                     this.execCmd('InsertText','\t');
25843                     this.deferFocus();
25844                     return;
25845                 }
25846                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25847                     this.cleanUpPaste.defer(100, this);
25848                     return;
25849                 }
25850                 
25851              };
25852         }
25853     }(),
25854     
25855     getAllAncestors: function()
25856     {
25857         var p = this.getSelectedNode();
25858         var a = [];
25859         if (!p) {
25860             a.push(p); // push blank onto stack..
25861             p = this.getParentElement();
25862         }
25863         
25864         
25865         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25866             a.push(p);
25867             p = p.parentNode;
25868         }
25869         a.push(this.doc.body);
25870         return a;
25871     },
25872     lastSel : false,
25873     lastSelNode : false,
25874     
25875     
25876     getSelection : function() 
25877     {
25878         this.assignDocWin();
25879         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25880     },
25881     
25882     getSelectedNode: function() 
25883     {
25884         // this may only work on Gecko!!!
25885         
25886         // should we cache this!!!!
25887         
25888         
25889         
25890          
25891         var range = this.createRange(this.getSelection()).cloneRange();
25892         
25893         if (Roo.isIE) {
25894             var parent = range.parentElement();
25895             while (true) {
25896                 var testRange = range.duplicate();
25897                 testRange.moveToElementText(parent);
25898                 if (testRange.inRange(range)) {
25899                     break;
25900                 }
25901                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25902                     break;
25903                 }
25904                 parent = parent.parentElement;
25905             }
25906             return parent;
25907         }
25908         
25909         // is ancestor a text element.
25910         var ac =  range.commonAncestorContainer;
25911         if (ac.nodeType == 3) {
25912             ac = ac.parentNode;
25913         }
25914         
25915         var ar = ac.childNodes;
25916          
25917         var nodes = [];
25918         var other_nodes = [];
25919         var has_other_nodes = false;
25920         for (var i=0;i<ar.length;i++) {
25921             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25922                 continue;
25923             }
25924             // fullly contained node.
25925             
25926             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25927                 nodes.push(ar[i]);
25928                 continue;
25929             }
25930             
25931             // probably selected..
25932             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25933                 other_nodes.push(ar[i]);
25934                 continue;
25935             }
25936             // outer..
25937             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25938                 continue;
25939             }
25940             
25941             
25942             has_other_nodes = true;
25943         }
25944         if (!nodes.length && other_nodes.length) {
25945             nodes= other_nodes;
25946         }
25947         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25948             return false;
25949         }
25950         
25951         return nodes[0];
25952     },
25953     createRange: function(sel)
25954     {
25955         // this has strange effects when using with 
25956         // top toolbar - not sure if it's a great idea.
25957         //this.editor.contentWindow.focus();
25958         if (typeof sel != "undefined") {
25959             try {
25960                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25961             } catch(e) {
25962                 return this.doc.createRange();
25963             }
25964         } else {
25965             return this.doc.createRange();
25966         }
25967     },
25968     getParentElement: function()
25969     {
25970         
25971         this.assignDocWin();
25972         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25973         
25974         var range = this.createRange(sel);
25975          
25976         try {
25977             var p = range.commonAncestorContainer;
25978             while (p.nodeType == 3) { // text node
25979                 p = p.parentNode;
25980             }
25981             return p;
25982         } catch (e) {
25983             return null;
25984         }
25985     
25986     },
25987     /***
25988      *
25989      * Range intersection.. the hard stuff...
25990      *  '-1' = before
25991      *  '0' = hits..
25992      *  '1' = after.
25993      *         [ -- selected range --- ]
25994      *   [fail]                        [fail]
25995      *
25996      *    basically..
25997      *      if end is before start or  hits it. fail.
25998      *      if start is after end or hits it fail.
25999      *
26000      *   if either hits (but other is outside. - then it's not 
26001      *   
26002      *    
26003      **/
26004     
26005     
26006     // @see http://www.thismuchiknow.co.uk/?p=64.
26007     rangeIntersectsNode : function(range, node)
26008     {
26009         var nodeRange = node.ownerDocument.createRange();
26010         try {
26011             nodeRange.selectNode(node);
26012         } catch (e) {
26013             nodeRange.selectNodeContents(node);
26014         }
26015     
26016         var rangeStartRange = range.cloneRange();
26017         rangeStartRange.collapse(true);
26018     
26019         var rangeEndRange = range.cloneRange();
26020         rangeEndRange.collapse(false);
26021     
26022         var nodeStartRange = nodeRange.cloneRange();
26023         nodeStartRange.collapse(true);
26024     
26025         var nodeEndRange = nodeRange.cloneRange();
26026         nodeEndRange.collapse(false);
26027     
26028         return rangeStartRange.compareBoundaryPoints(
26029                  Range.START_TO_START, nodeEndRange) == -1 &&
26030                rangeEndRange.compareBoundaryPoints(
26031                  Range.START_TO_START, nodeStartRange) == 1;
26032         
26033          
26034     },
26035     rangeCompareNode : function(range, node)
26036     {
26037         var nodeRange = node.ownerDocument.createRange();
26038         try {
26039             nodeRange.selectNode(node);
26040         } catch (e) {
26041             nodeRange.selectNodeContents(node);
26042         }
26043         
26044         
26045         range.collapse(true);
26046     
26047         nodeRange.collapse(true);
26048      
26049         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26050         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26051          
26052         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26053         
26054         var nodeIsBefore   =  ss == 1;
26055         var nodeIsAfter    = ee == -1;
26056         
26057         if (nodeIsBefore && nodeIsAfter)
26058             return 0; // outer
26059         if (!nodeIsBefore && nodeIsAfter)
26060             return 1; //right trailed.
26061         
26062         if (nodeIsBefore && !nodeIsAfter)
26063             return 2;  // left trailed.
26064         // fully contined.
26065         return 3;
26066     },
26067
26068     // private? - in a new class?
26069     cleanUpPaste :  function()
26070     {
26071         // cleans up the whole document..
26072          Roo.log('cleanuppaste');
26073         this.cleanUpChildren(this.doc.body);
26074         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26075         if (clean != this.doc.body.innerHTML) {
26076             this.doc.body.innerHTML = clean;
26077         }
26078         
26079     },
26080     
26081     cleanWordChars : function(input) {
26082         var he = Roo.form.HtmlEditor;
26083     
26084         var output = input;
26085         Roo.each(he.swapCodes, function(sw) { 
26086         
26087             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26088             output = output.replace(swapper, sw[1]);
26089         });
26090         return output;
26091     },
26092     
26093     
26094     cleanUpChildren : function (n)
26095     {
26096         if (!n.childNodes.length) {
26097             return;
26098         }
26099         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26100            this.cleanUpChild(n.childNodes[i]);
26101         }
26102     },
26103     
26104     
26105         
26106     
26107     cleanUpChild : function (node)
26108     {
26109         //console.log(node);
26110         if (node.nodeName == "#text") {
26111             // clean up silly Windows -- stuff?
26112             return; 
26113         }
26114         if (node.nodeName == "#comment") {
26115             node.parentNode.removeChild(node);
26116             // clean up silly Windows -- stuff?
26117             return; 
26118         }
26119         
26120         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
26121             // remove node.
26122             node.parentNode.removeChild(node);
26123             return;
26124             
26125         }
26126         
26127         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
26128         
26129         // remove <a name=....> as rendering on yahoo mailer is bored with this.
26130         
26131         if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26132             remove_keep_children = true;
26133         }
26134         
26135         if (remove_keep_children) {
26136             this.cleanUpChildren(node);
26137             // inserts everything just before this node...
26138             while (node.childNodes.length) {
26139                 var cn = node.childNodes[0];
26140                 node.removeChild(cn);
26141                 node.parentNode.insertBefore(cn, node);
26142             }
26143             node.parentNode.removeChild(node);
26144             return;
26145         }
26146         
26147         if (!node.attributes || !node.attributes.length) {
26148             this.cleanUpChildren(node);
26149             return;
26150         }
26151         
26152         function cleanAttr(n,v)
26153         {
26154             
26155             if (v.match(/^\./) || v.match(/^\//)) {
26156                 return;
26157             }
26158             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
26159                 return;
26160             }
26161             if (v.match(/^#/)) {
26162                 return;
26163             }
26164             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
26165             node.removeAttribute(n);
26166             
26167         }
26168         
26169         function cleanStyle(n,v)
26170         {
26171             if (v.match(/expression/)) { //XSS?? should we even bother..
26172                 node.removeAttribute(n);
26173                 return;
26174             }
26175             
26176             
26177             var parts = v.split(/;/);
26178             Roo.each(parts, function(p) {
26179                 p = p.replace(/\s+/g,'');
26180                 if (!p.length) {
26181                     return true;
26182                 }
26183                 var l = p.split(':').shift().replace(/\s+/g,'');
26184                 
26185                 // only allow 'c whitelisted system attributes'
26186                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
26187                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
26188                     node.removeAttribute(n);
26189                     return false;
26190                 }
26191                 return true;
26192             });
26193             
26194             
26195         }
26196         
26197         
26198         for (var i = node.attributes.length-1; i > -1 ; i--) {
26199             var a = node.attributes[i];
26200             //console.log(a);
26201             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
26202                 node.removeAttribute(a.name);
26203                 continue;
26204             }
26205             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
26206                 cleanAttr(a.name,a.value); // fixme..
26207                 continue;
26208             }
26209             if (a.name == 'style') {
26210                 cleanStyle(a.name,a.value);
26211                 continue;
26212             }
26213             /// clean up MS crap..
26214             // tecnically this should be a list of valid class'es..
26215             
26216             
26217             if (a.name == 'class') {
26218                 if (a.value.match(/^Mso/)) {
26219                     node.className = '';
26220                 }
26221                 
26222                 if (a.value.match(/body/)) {
26223                     node.className = '';
26224                 }
26225                 continue;
26226             }
26227             
26228             // style cleanup!?
26229             // class cleanup?
26230             
26231         }
26232         
26233         
26234         this.cleanUpChildren(node);
26235         
26236         
26237     }
26238     
26239     
26240     // hide stuff that is not compatible
26241     /**
26242      * @event blur
26243      * @hide
26244      */
26245     /**
26246      * @event change
26247      * @hide
26248      */
26249     /**
26250      * @event focus
26251      * @hide
26252      */
26253     /**
26254      * @event specialkey
26255      * @hide
26256      */
26257     /**
26258      * @cfg {String} fieldClass @hide
26259      */
26260     /**
26261      * @cfg {String} focusClass @hide
26262      */
26263     /**
26264      * @cfg {String} autoCreate @hide
26265      */
26266     /**
26267      * @cfg {String} inputType @hide
26268      */
26269     /**
26270      * @cfg {String} invalidClass @hide
26271      */
26272     /**
26273      * @cfg {String} invalidText @hide
26274      */
26275     /**
26276      * @cfg {String} msgFx @hide
26277      */
26278     /**
26279      * @cfg {String} validateOnBlur @hide
26280      */
26281 });
26282
26283 Roo.form.HtmlEditor.white = [
26284         'area', 'br', 'img', 'input', 'hr', 'wbr',
26285         
26286        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26287        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26288        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26289        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26290        'table',   'ul',         'xmp', 
26291        
26292        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26293       'thead',   'tr', 
26294      
26295       'dir', 'menu', 'ol', 'ul', 'dl',
26296        
26297       'embed',  'object'
26298 ];
26299
26300
26301 Roo.form.HtmlEditor.black = [
26302     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26303         'applet', // 
26304         'base',   'basefont', 'bgsound', 'blink',  'body', 
26305         'frame',  'frameset', 'head',    'html',   'ilayer', 
26306         'iframe', 'layer',  'link',     'meta',    'object',   
26307         'script', 'style' ,'title',  'xml' // clean later..
26308 ];
26309 Roo.form.HtmlEditor.clean = [
26310     'script', 'style', 'title', 'xml'
26311 ];
26312 Roo.form.HtmlEditor.remove = [
26313     'font'
26314 ];
26315 // attributes..
26316
26317 Roo.form.HtmlEditor.ablack = [
26318     'on'
26319 ];
26320     
26321 Roo.form.HtmlEditor.aclean = [ 
26322     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
26323 ];
26324
26325 // protocols..
26326 Roo.form.HtmlEditor.pwhite= [
26327         'http',  'https',  'mailto'
26328 ];
26329
26330 // white listed style attributes.
26331 Roo.form.HtmlEditor.cwhite= [
26332         'text-align',
26333         'font-size'
26334 ];
26335
26336
26337 Roo.form.HtmlEditor.swapCodes   =[ 
26338     [    8211, "--" ], 
26339     [    8212, "--" ], 
26340     [    8216,  "'" ],  
26341     [    8217, "'" ],  
26342     [    8220, '"' ],  
26343     [    8221, '"' ],  
26344     [    8226, "*" ],  
26345     [    8230, "..." ]
26346 ]; 
26347
26348     // <script type="text/javascript">
26349 /*
26350  * Based on
26351  * Ext JS Library 1.1.1
26352  * Copyright(c) 2006-2007, Ext JS, LLC.
26353  *  
26354  
26355  */
26356
26357 /**
26358  * @class Roo.form.HtmlEditorToolbar1
26359  * Basic Toolbar
26360  * 
26361  * Usage:
26362  *
26363  new Roo.form.HtmlEditor({
26364     ....
26365     toolbars : [
26366         new Roo.form.HtmlEditorToolbar1({
26367             disable : { fonts: 1 , format: 1, ..., ... , ...],
26368             btns : [ .... ]
26369         })
26370     }
26371      
26372  * 
26373  * @cfg {Object} disable List of elements to disable..
26374  * @cfg {Array} btns List of additional buttons.
26375  * 
26376  * 
26377  * NEEDS Extra CSS? 
26378  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26379  */
26380  
26381 Roo.form.HtmlEditor.ToolbarStandard = function(config)
26382 {
26383     
26384     Roo.apply(this, config);
26385     
26386     // default disabled, based on 'good practice'..
26387     this.disable = this.disable || {};
26388     Roo.applyIf(this.disable, {
26389         fontSize : true,
26390         colors : true,
26391         specialElements : true
26392     });
26393     
26394     
26395     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26396     // dont call parent... till later.
26397 }
26398
26399 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
26400     
26401     tb: false,
26402     
26403     rendered: false,
26404     
26405     editor : false,
26406     /**
26407      * @cfg {Object} disable  List of toolbar elements to disable
26408          
26409      */
26410     disable : false,
26411       /**
26412      * @cfg {Array} fontFamilies An array of available font families
26413      */
26414     fontFamilies : [
26415         'Arial',
26416         'Courier New',
26417         'Tahoma',
26418         'Times New Roman',
26419         'Verdana'
26420     ],
26421     
26422     specialChars : [
26423            "&#169;",
26424           "&#174;",     
26425           "&#8482;",    
26426           "&#163;" ,    
26427          // "&#8212;",    
26428           "&#8230;",    
26429           "&#247;" ,    
26430         //  "&#225;" ,     ?? a acute?
26431            "&#8364;"    , //Euro
26432        //   "&#8220;"    ,
26433         //  "&#8221;"    ,
26434         //  "&#8226;"    ,
26435           "&#176;"  //   , // degrees
26436
26437          // "&#233;"     , // e ecute
26438          // "&#250;"     , // u ecute?
26439     ],
26440     
26441     specialElements : [
26442         {
26443             text: "Insert Table",
26444             xtype: 'MenuItem',
26445             xns : Roo.Menu,
26446             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
26447                 
26448         },
26449         {    
26450             text: "Insert Image",
26451             xtype: 'MenuItem',
26452             xns : Roo.Menu,
26453             ihtml : '<img src="about:blank"/>'
26454             
26455         }
26456         
26457          
26458     ],
26459     
26460     
26461     inputElements : [ 
26462             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
26463             "input:submit", "input:button", "select", "textarea", "label" ],
26464     formats : [
26465         ["p"] ,  
26466         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
26467         ["pre"],[ "code"], 
26468         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
26469     ],
26470      /**
26471      * @cfg {String} defaultFont default font to use.
26472      */
26473     defaultFont: 'tahoma',
26474    
26475     fontSelect : false,
26476     
26477     
26478     formatCombo : false,
26479     
26480     init : function(editor)
26481     {
26482         this.editor = editor;
26483         
26484         
26485         var fid = editor.frameId;
26486         var etb = this;
26487         function btn(id, toggle, handler){
26488             var xid = fid + '-'+ id ;
26489             return {
26490                 id : xid,
26491                 cmd : id,
26492                 cls : 'x-btn-icon x-edit-'+id,
26493                 enableToggle:toggle !== false,
26494                 scope: editor, // was editor...
26495                 handler:handler||editor.relayBtnCmd,
26496                 clickEvent:'mousedown',
26497                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
26498                 tabIndex:-1
26499             };
26500         }
26501         
26502         
26503         
26504         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
26505         this.tb = tb;
26506          // stop form submits
26507         tb.el.on('click', function(e){
26508             e.preventDefault(); // what does this do?
26509         });
26510
26511         if(!this.disable.font && !Roo.isSafari){
26512             /* why no safari for fonts
26513             editor.fontSelect = tb.el.createChild({
26514                 tag:'select',
26515                 tabIndex: -1,
26516                 cls:'x-font-select',
26517                 html: editor.createFontOptions()
26518             });
26519             editor.fontSelect.on('change', function(){
26520                 var font = editor.fontSelect.dom.value;
26521                 editor.relayCmd('fontname', font);
26522                 editor.deferFocus();
26523             }, editor);
26524             tb.add(
26525                 editor.fontSelect.dom,
26526                 '-'
26527             );
26528             */
26529         };
26530         if(!this.disable.formats){
26531             this.formatCombo = new Roo.form.ComboBox({
26532                 store: new Roo.data.SimpleStore({
26533                     id : 'tag',
26534                     fields: ['tag'],
26535                     data : this.formats // from states.js
26536                 }),
26537                 blockFocus : true,
26538                 //autoCreate : {tag: "div",  size: "20"},
26539                 displayField:'tag',
26540                 typeAhead: false,
26541                 mode: 'local',
26542                 editable : false,
26543                 triggerAction: 'all',
26544                 emptyText:'Add tag',
26545                 selectOnFocus:true,
26546                 width:135,
26547                 listeners : {
26548                     'select': function(c, r, i) {
26549                         editor.insertTag(r.get('tag'));
26550                         editor.focus();
26551                     }
26552                 }
26553
26554             });
26555             tb.addField(this.formatCombo);
26556             
26557         }
26558         
26559         if(!this.disable.format){
26560             tb.add(
26561                 btn('bold'),
26562                 btn('italic'),
26563                 btn('underline')
26564             );
26565         };
26566         if(!this.disable.fontSize){
26567             tb.add(
26568                 '-',
26569                 
26570                 
26571                 btn('increasefontsize', false, editor.adjustFont),
26572                 btn('decreasefontsize', false, editor.adjustFont)
26573             );
26574         };
26575         
26576         
26577         if(!this.disable.colors){
26578             tb.add(
26579                 '-', {
26580                     id:editor.frameId +'-forecolor',
26581                     cls:'x-btn-icon x-edit-forecolor',
26582                     clickEvent:'mousedown',
26583                     tooltip: this.buttonTips['forecolor'] || undefined,
26584                     tabIndex:-1,
26585                     menu : new Roo.menu.ColorMenu({
26586                         allowReselect: true,
26587                         focus: Roo.emptyFn,
26588                         value:'000000',
26589                         plain:true,
26590                         selectHandler: function(cp, color){
26591                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
26592                             editor.deferFocus();
26593                         },
26594                         scope: editor,
26595                         clickEvent:'mousedown'
26596                     })
26597                 }, {
26598                     id:editor.frameId +'backcolor',
26599                     cls:'x-btn-icon x-edit-backcolor',
26600                     clickEvent:'mousedown',
26601                     tooltip: this.buttonTips['backcolor'] || undefined,
26602                     tabIndex:-1,
26603                     menu : new Roo.menu.ColorMenu({
26604                         focus: Roo.emptyFn,
26605                         value:'FFFFFF',
26606                         plain:true,
26607                         allowReselect: true,
26608                         selectHandler: function(cp, color){
26609                             if(Roo.isGecko){
26610                                 editor.execCmd('useCSS', false);
26611                                 editor.execCmd('hilitecolor', color);
26612                                 editor.execCmd('useCSS', true);
26613                                 editor.deferFocus();
26614                             }else{
26615                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
26616                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
26617                                 editor.deferFocus();
26618                             }
26619                         },
26620                         scope:editor,
26621                         clickEvent:'mousedown'
26622                     })
26623                 }
26624             );
26625         };
26626         // now add all the items...
26627         
26628
26629         if(!this.disable.alignments){
26630             tb.add(
26631                 '-',
26632                 btn('justifyleft'),
26633                 btn('justifycenter'),
26634                 btn('justifyright')
26635             );
26636         };
26637
26638         //if(!Roo.isSafari){
26639             if(!this.disable.links){
26640                 tb.add(
26641                     '-',
26642                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
26643                 );
26644             };
26645
26646             if(!this.disable.lists){
26647                 tb.add(
26648                     '-',
26649                     btn('insertorderedlist'),
26650                     btn('insertunorderedlist')
26651                 );
26652             }
26653             if(!this.disable.sourceEdit){
26654                 tb.add(
26655                     '-',
26656                     btn('sourceedit', true, function(btn){
26657                         this.toggleSourceEdit(btn.pressed);
26658                     })
26659                 );
26660             }
26661         //}
26662         
26663         var smenu = { };
26664         // special menu.. - needs to be tidied up..
26665         if (!this.disable.special) {
26666             smenu = {
26667                 text: "&#169;",
26668                 cls: 'x-edit-none',
26669                 
26670                 menu : {
26671                     items : []
26672                 }
26673             };
26674             for (var i =0; i < this.specialChars.length; i++) {
26675                 smenu.menu.items.push({
26676                     
26677                     html: this.specialChars[i],
26678                     handler: function(a,b) {
26679                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
26680                         //editor.insertAtCursor(a.html);
26681                         
26682                     },
26683                     tabIndex:-1
26684                 });
26685             }
26686             
26687             
26688             tb.add(smenu);
26689             
26690             
26691         }
26692          
26693         if (!this.disable.specialElements) {
26694             var semenu = {
26695                 text: "Other;",
26696                 cls: 'x-edit-none',
26697                 menu : {
26698                     items : []
26699                 }
26700             };
26701             for (var i =0; i < this.specialElements.length; i++) {
26702                 semenu.menu.items.push(
26703                     Roo.apply({ 
26704                         handler: function(a,b) {
26705                             editor.insertAtCursor(this.ihtml);
26706                         }
26707                     }, this.specialElements[i])
26708                 );
26709                     
26710             }
26711             
26712             tb.add(semenu);
26713             
26714             
26715         }
26716          
26717         
26718         if (this.btns) {
26719             for(var i =0; i< this.btns.length;i++) {
26720                 var b = Roo.factory(this.btns[i],Roo.form);
26721                 b.cls =  'x-edit-none';
26722                 b.scope = editor;
26723                 tb.add(b);
26724             }
26725         
26726         }
26727         
26728         
26729         
26730         // disable everything...
26731         
26732         this.tb.items.each(function(item){
26733            if(item.id != editor.frameId+ '-sourceedit'){
26734                 item.disable();
26735             }
26736         });
26737         this.rendered = true;
26738         
26739         // the all the btns;
26740         editor.on('editorevent', this.updateToolbar, this);
26741         // other toolbars need to implement this..
26742         //editor.on('editmodechange', this.updateToolbar, this);
26743     },
26744     
26745     
26746     
26747     /**
26748      * Protected method that will not generally be called directly. It triggers
26749      * a toolbar update by reading the markup state of the current selection in the editor.
26750      */
26751     updateToolbar: function(){
26752
26753         if(!this.editor.activated){
26754             this.editor.onFirstFocus();
26755             return;
26756         }
26757
26758         var btns = this.tb.items.map, 
26759             doc = this.editor.doc,
26760             frameId = this.editor.frameId;
26761
26762         if(!this.disable.font && !Roo.isSafari){
26763             /*
26764             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
26765             if(name != this.fontSelect.dom.value){
26766                 this.fontSelect.dom.value = name;
26767             }
26768             */
26769         }
26770         if(!this.disable.format){
26771             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
26772             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
26773             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
26774         }
26775         if(!this.disable.alignments){
26776             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
26777             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
26778             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
26779         }
26780         if(!Roo.isSafari && !this.disable.lists){
26781             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
26782             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
26783         }
26784         
26785         var ans = this.editor.getAllAncestors();
26786         if (this.formatCombo) {
26787             
26788             
26789             var store = this.formatCombo.store;
26790             this.formatCombo.setValue("");
26791             for (var i =0; i < ans.length;i++) {
26792                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26793                     // select it..
26794                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26795                     break;
26796                 }
26797             }
26798         }
26799         
26800         
26801         
26802         // hides menus... - so this cant be on a menu...
26803         Roo.menu.MenuMgr.hideAll();
26804
26805         //this.editorsyncValue();
26806     },
26807    
26808     
26809     createFontOptions : function(){
26810         var buf = [], fs = this.fontFamilies, ff, lc;
26811         for(var i = 0, len = fs.length; i< len; i++){
26812             ff = fs[i];
26813             lc = ff.toLowerCase();
26814             buf.push(
26815                 '<option value="',lc,'" style="font-family:',ff,';"',
26816                     (this.defaultFont == lc ? ' selected="true">' : '>'),
26817                     ff,
26818                 '</option>'
26819             );
26820         }
26821         return buf.join('');
26822     },
26823     
26824     toggleSourceEdit : function(sourceEditMode){
26825         if(sourceEditMode === undefined){
26826             sourceEditMode = !this.sourceEditMode;
26827         }
26828         this.sourceEditMode = sourceEditMode === true;
26829         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
26830         // just toggle the button?
26831         if(btn.pressed !== this.editor.sourceEditMode){
26832             btn.toggle(this.editor.sourceEditMode);
26833             return;
26834         }
26835         
26836         if(this.sourceEditMode){
26837             this.tb.items.each(function(item){
26838                 if(item.cmd != 'sourceedit'){
26839                     item.disable();
26840                 }
26841             });
26842           
26843         }else{
26844             if(this.initialized){
26845                 this.tb.items.each(function(item){
26846                     item.enable();
26847                 });
26848             }
26849             
26850         }
26851         // tell the editor that it's been pressed..
26852         this.editor.toggleSourceEdit(sourceEditMode);
26853        
26854     },
26855      /**
26856      * Object collection of toolbar tooltips for the buttons in the editor. The key
26857      * is the command id associated with that button and the value is a valid QuickTips object.
26858      * For example:
26859 <pre><code>
26860 {
26861     bold : {
26862         title: 'Bold (Ctrl+B)',
26863         text: 'Make the selected text bold.',
26864         cls: 'x-html-editor-tip'
26865     },
26866     italic : {
26867         title: 'Italic (Ctrl+I)',
26868         text: 'Make the selected text italic.',
26869         cls: 'x-html-editor-tip'
26870     },
26871     ...
26872 </code></pre>
26873     * @type Object
26874      */
26875     buttonTips : {
26876         bold : {
26877             title: 'Bold (Ctrl+B)',
26878             text: 'Make the selected text bold.',
26879             cls: 'x-html-editor-tip'
26880         },
26881         italic : {
26882             title: 'Italic (Ctrl+I)',
26883             text: 'Make the selected text italic.',
26884             cls: 'x-html-editor-tip'
26885         },
26886         underline : {
26887             title: 'Underline (Ctrl+U)',
26888             text: 'Underline the selected text.',
26889             cls: 'x-html-editor-tip'
26890         },
26891         increasefontsize : {
26892             title: 'Grow Text',
26893             text: 'Increase the font size.',
26894             cls: 'x-html-editor-tip'
26895         },
26896         decreasefontsize : {
26897             title: 'Shrink Text',
26898             text: 'Decrease the font size.',
26899             cls: 'x-html-editor-tip'
26900         },
26901         backcolor : {
26902             title: 'Text Highlight Color',
26903             text: 'Change the background color of the selected text.',
26904             cls: 'x-html-editor-tip'
26905         },
26906         forecolor : {
26907             title: 'Font Color',
26908             text: 'Change the color of the selected text.',
26909             cls: 'x-html-editor-tip'
26910         },
26911         justifyleft : {
26912             title: 'Align Text Left',
26913             text: 'Align text to the left.',
26914             cls: 'x-html-editor-tip'
26915         },
26916         justifycenter : {
26917             title: 'Center Text',
26918             text: 'Center text in the editor.',
26919             cls: 'x-html-editor-tip'
26920         },
26921         justifyright : {
26922             title: 'Align Text Right',
26923             text: 'Align text to the right.',
26924             cls: 'x-html-editor-tip'
26925         },
26926         insertunorderedlist : {
26927             title: 'Bullet List',
26928             text: 'Start a bulleted list.',
26929             cls: 'x-html-editor-tip'
26930         },
26931         insertorderedlist : {
26932             title: 'Numbered List',
26933             text: 'Start a numbered list.',
26934             cls: 'x-html-editor-tip'
26935         },
26936         createlink : {
26937             title: 'Hyperlink',
26938             text: 'Make the selected text a hyperlink.',
26939             cls: 'x-html-editor-tip'
26940         },
26941         sourceedit : {
26942             title: 'Source Edit',
26943             text: 'Switch to source editing mode.',
26944             cls: 'x-html-editor-tip'
26945         }
26946     },
26947     // private
26948     onDestroy : function(){
26949         if(this.rendered){
26950             
26951             this.tb.items.each(function(item){
26952                 if(item.menu){
26953                     item.menu.removeAll();
26954                     if(item.menu.el){
26955                         item.menu.el.destroy();
26956                     }
26957                 }
26958                 item.destroy();
26959             });
26960              
26961         }
26962     },
26963     onFirstFocus: function() {
26964         this.tb.items.each(function(item){
26965            item.enable();
26966         });
26967     }
26968 });
26969
26970
26971
26972
26973 // <script type="text/javascript">
26974 /*
26975  * Based on
26976  * Ext JS Library 1.1.1
26977  * Copyright(c) 2006-2007, Ext JS, LLC.
26978  *  
26979  
26980  */
26981
26982  
26983 /**
26984  * @class Roo.form.HtmlEditor.ToolbarContext
26985  * Context Toolbar
26986  * 
26987  * Usage:
26988  *
26989  new Roo.form.HtmlEditor({
26990     ....
26991     toolbars : [
26992         { xtype: 'ToolbarStandard', styles : {} }
26993         { xtype: 'ToolbarContext', disable : {} }
26994     ]
26995 })
26996
26997      
26998  * 
26999  * @config : {Object} disable List of elements to disable.. (not done yet.)
27000  * @config : {Object} styles  Map of styles available.
27001  * 
27002  */
27003
27004 Roo.form.HtmlEditor.ToolbarContext = function(config)
27005 {
27006     
27007     Roo.apply(this, config);
27008     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27009     // dont call parent... till later.
27010     this.styles = this.styles || {};
27011 }
27012 Roo.form.HtmlEditor.ToolbarContext.types = {
27013     'IMG' : {
27014         width : {
27015             title: "Width",
27016             width: 40
27017         },
27018         height:  {
27019             title: "Height",
27020             width: 40
27021         },
27022         align: {
27023             title: "Align",
27024             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
27025             width : 80
27026             
27027         },
27028         border: {
27029             title: "Border",
27030             width: 40
27031         },
27032         alt: {
27033             title: "Alt",
27034             width: 120
27035         },
27036         src : {
27037             title: "Src",
27038             width: 220
27039         }
27040         
27041     },
27042     'A' : {
27043         name : {
27044             title: "Name",
27045             width: 50
27046         },
27047         href:  {
27048             title: "Href",
27049             width: 220
27050         } // border?
27051         
27052     },
27053     'TABLE' : {
27054         rows : {
27055             title: "Rows",
27056             width: 20
27057         },
27058         cols : {
27059             title: "Cols",
27060             width: 20
27061         },
27062         width : {
27063             title: "Width",
27064             width: 40
27065         },
27066         height : {
27067             title: "Height",
27068             width: 40
27069         },
27070         border : {
27071             title: "Border",
27072             width: 20
27073         }
27074     },
27075     'TD' : {
27076         width : {
27077             title: "Width",
27078             width: 40
27079         },
27080         height : {
27081             title: "Height",
27082             width: 40
27083         },   
27084         align: {
27085             title: "Align",
27086             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
27087             width: 80
27088         },
27089         valign: {
27090             title: "Valign",
27091             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
27092             width: 80
27093         },
27094         colspan: {
27095             title: "Colspan",
27096             width: 20
27097             
27098         }
27099     },
27100     'INPUT' : {
27101         name : {
27102             title: "name",
27103             width: 120
27104         },
27105         value : {
27106             title: "Value",
27107             width: 120
27108         },
27109         width : {
27110             title: "Width",
27111             width: 40
27112         }
27113     },
27114     'LABEL' : {
27115         'for' : {
27116             title: "For",
27117             width: 120
27118         }
27119     },
27120     'TEXTAREA' : {
27121           name : {
27122             title: "name",
27123             width: 120
27124         },
27125         rows : {
27126             title: "Rows",
27127             width: 20
27128         },
27129         cols : {
27130             title: "Cols",
27131             width: 20
27132         }
27133     },
27134     'SELECT' : {
27135         name : {
27136             title: "name",
27137             width: 120
27138         },
27139         selectoptions : {
27140             title: "Options",
27141             width: 200
27142         }
27143     },
27144     
27145     // should we really allow this??
27146     // should this just be 
27147     'BODY' : {
27148         title : {
27149             title: "title",
27150             width: 200,
27151             disabled : true
27152         }
27153     },
27154     '*' : {
27155         // empty..
27156     }
27157 };
27158
27159
27160
27161 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
27162     
27163     tb: false,
27164     
27165     rendered: false,
27166     
27167     editor : false,
27168     /**
27169      * @cfg {Object} disable  List of toolbar elements to disable
27170          
27171      */
27172     disable : false,
27173     /**
27174      * @cfg {Object} styles List of styles 
27175      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
27176      *
27177      * These must be defined in the page, so they get rendered correctly..
27178      * .headline { }
27179      * TD.underline { }
27180      * 
27181      */
27182     styles : false,
27183     
27184     
27185     
27186     toolbars : false,
27187     
27188     init : function(editor)
27189     {
27190         this.editor = editor;
27191         
27192         
27193         var fid = editor.frameId;
27194         var etb = this;
27195         function btn(id, toggle, handler){
27196             var xid = fid + '-'+ id ;
27197             return {
27198                 id : xid,
27199                 cmd : id,
27200                 cls : 'x-btn-icon x-edit-'+id,
27201                 enableToggle:toggle !== false,
27202                 scope: editor, // was editor...
27203                 handler:handler||editor.relayBtnCmd,
27204                 clickEvent:'mousedown',
27205                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27206                 tabIndex:-1
27207             };
27208         }
27209         // create a new element.
27210         var wdiv = editor.wrap.createChild({
27211                 tag: 'div'
27212             }, editor.wrap.dom.firstChild.nextSibling, true);
27213         
27214         // can we do this more than once??
27215         
27216          // stop form submits
27217       
27218  
27219         // disable everything...
27220         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27221         this.toolbars = {};
27222            
27223         for (var i in  ty) {
27224           
27225             this.toolbars[i] = this.buildToolbar(ty[i],i);
27226         }
27227         this.tb = this.toolbars.BODY;
27228         this.tb.el.show();
27229         this.buildFooter();
27230         this.footer.show();
27231         editor.on('hide', function( ) { this.footer.hide() }, this);
27232         editor.on('show', function( ) { this.footer.show() }, this);
27233         
27234          
27235         this.rendered = true;
27236         
27237         // the all the btns;
27238         editor.on('editorevent', this.updateToolbar, this);
27239         // other toolbars need to implement this..
27240         //editor.on('editmodechange', this.updateToolbar, this);
27241     },
27242     
27243     
27244     
27245     /**
27246      * Protected method that will not generally be called directly. It triggers
27247      * a toolbar update by reading the markup state of the current selection in the editor.
27248      */
27249     updateToolbar: function(editor,ev,sel){
27250
27251         //Roo.log(ev);
27252         // capture mouse up - this is handy for selecting images..
27253         // perhaps should go somewhere else...
27254         if(!this.editor.activated){
27255              this.editor.onFirstFocus();
27256             return;
27257         }
27258         
27259         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
27260         // selectNode - might want to handle IE?
27261         if (ev &&
27262             (ev.type == 'mouseup' || ev.type == 'click' ) &&
27263             ev.target && ev.target.tagName == 'IMG') {
27264             // they have click on an image...
27265             // let's see if we can change the selection...
27266             sel = ev.target;
27267          
27268               var nodeRange = sel.ownerDocument.createRange();
27269             try {
27270                 nodeRange.selectNode(sel);
27271             } catch (e) {
27272                 nodeRange.selectNodeContents(sel);
27273             }
27274             //nodeRange.collapse(true);
27275             var s = editor.win.getSelection();
27276             s.removeAllRanges();
27277             s.addRange(nodeRange);
27278         }  
27279         
27280       
27281         var updateFooter = sel ? false : true;
27282         
27283         
27284         var ans = this.editor.getAllAncestors();
27285         
27286         // pick
27287         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27288         
27289         if (!sel) { 
27290             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
27291             sel = sel ? sel : this.editor.doc.body;
27292             sel = sel.tagName.length ? sel : this.editor.doc.body;
27293             
27294         }
27295         // pick a menu that exists..
27296         var tn = sel.tagName.toUpperCase();
27297         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
27298         
27299         tn = sel.tagName.toUpperCase();
27300         
27301         var lastSel = this.tb.selectedNode
27302         
27303         this.tb.selectedNode = sel;
27304         
27305         // if current menu does not match..
27306         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
27307                 
27308             this.tb.el.hide();
27309             ///console.log("show: " + tn);
27310             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
27311             this.tb.el.show();
27312             // update name
27313             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
27314             
27315             
27316             // update attributes
27317             if (this.tb.fields) {
27318                 this.tb.fields.each(function(e) {
27319                    e.setValue(sel.getAttribute(e.attrname));
27320                 });
27321             }
27322             
27323             var hasStyles = false;
27324             for(var i in this.styles) {
27325                 hasStyles = true;
27326                 break;
27327             }
27328             
27329             // update styles
27330             if (hasStyles) { 
27331                 var st = this.tb.fields.item(0);
27332                 
27333                 st.store.removeAll();
27334                
27335                 
27336                 var cn = sel.className.split(/\s+/);
27337                 
27338                 var avs = [];
27339                 if (this.styles['*']) {
27340                     
27341                     Roo.each(this.styles['*'], function(v) {
27342                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27343                     });
27344                 }
27345                 if (this.styles[tn]) { 
27346                     Roo.each(this.styles[tn], function(v) {
27347                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27348                     });
27349                 }
27350                 
27351                 st.store.loadData(avs);
27352                 st.collapse();
27353                 st.setValue(cn);
27354             }
27355             // flag our selected Node.
27356             this.tb.selectedNode = sel;
27357            
27358            
27359             Roo.menu.MenuMgr.hideAll();
27360
27361         }
27362         
27363         if (!updateFooter) {
27364             return;
27365         }
27366         // update the footer
27367         //
27368         var html = '';
27369         
27370         this.footerEls = ans.reverse();
27371         Roo.each(this.footerEls, function(a,i) {
27372             if (!a) { return; }
27373             html += html.length ? ' &gt; '  :  '';
27374             
27375             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
27376             
27377         });
27378        
27379         // 
27380         var sz = this.footDisp.up('td').getSize();
27381         this.footDisp.dom.style.width = (sz.width -10) + 'px';
27382         this.footDisp.dom.style.marginLeft = '5px';
27383         
27384         this.footDisp.dom.style.overflow = 'hidden';
27385         
27386         this.footDisp.dom.innerHTML = html;
27387             
27388         //this.editorsyncValue();
27389     },
27390    
27391        
27392     // private
27393     onDestroy : function(){
27394         if(this.rendered){
27395             
27396             this.tb.items.each(function(item){
27397                 if(item.menu){
27398                     item.menu.removeAll();
27399                     if(item.menu.el){
27400                         item.menu.el.destroy();
27401                     }
27402                 }
27403                 item.destroy();
27404             });
27405              
27406         }
27407     },
27408     onFirstFocus: function() {
27409         // need to do this for all the toolbars..
27410         this.tb.items.each(function(item){
27411            item.enable();
27412         });
27413     },
27414     buildToolbar: function(tlist, nm)
27415     {
27416         var editor = this.editor;
27417          // create a new element.
27418         var wdiv = editor.wrap.createChild({
27419                 tag: 'div'
27420             }, editor.wrap.dom.firstChild.nextSibling, true);
27421         
27422        
27423         var tb = new Roo.Toolbar(wdiv);
27424         // add the name..
27425         
27426         tb.add(nm+ ":&nbsp;");
27427         
27428         var styles = [];
27429         for(var i in this.styles) {
27430             styles.push(i);
27431         }
27432         
27433         // styles...
27434         if (styles && styles.length) {
27435             
27436             // this needs a multi-select checkbox...
27437             tb.addField( new Roo.form.ComboBox({
27438                 store: new Roo.data.SimpleStore({
27439                     id : 'val',
27440                     fields: ['val', 'selected'],
27441                     data : [] 
27442                 }),
27443                 name : '-roo-edit-className',
27444                 attrname : 'className',
27445                 displayField:'val',
27446                 typeAhead: false,
27447                 mode: 'local',
27448                 editable : false,
27449                 triggerAction: 'all',
27450                 emptyText:'Select Style',
27451                 selectOnFocus:true,
27452                 width: 130,
27453                 listeners : {
27454                     'select': function(c, r, i) {
27455                         // initial support only for on class per el..
27456                         tb.selectedNode.className =  r ? r.get('val') : '';
27457                         editor.syncValue();
27458                     }
27459                 }
27460     
27461             }));
27462         }
27463             
27464         
27465         
27466         for (var i in tlist) {
27467             
27468             var item = tlist[i];
27469             tb.add(item.title + ":&nbsp;");
27470             
27471             
27472             
27473             
27474             if (item.opts) {
27475                 // opts == pulldown..
27476                 tb.addField( new Roo.form.ComboBox({
27477                     store: new Roo.data.SimpleStore({
27478                         id : 'val',
27479                         fields: ['val'],
27480                         data : item.opts  
27481                     }),
27482                     name : '-roo-edit-' + i,
27483                     attrname : i,
27484                     displayField:'val',
27485                     typeAhead: false,
27486                     mode: 'local',
27487                     editable : false,
27488                     triggerAction: 'all',
27489                     emptyText:'Select',
27490                     selectOnFocus:true,
27491                     width: item.width ? item.width  : 130,
27492                     listeners : {
27493                         'select': function(c, r, i) {
27494                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
27495                         }
27496                     }
27497
27498                 }));
27499                 continue;
27500                     
27501                  
27502                 
27503                 tb.addField( new Roo.form.TextField({
27504                     name: i,
27505                     width: 100,
27506                     //allowBlank:false,
27507                     value: ''
27508                 }));
27509                 continue;
27510             }
27511             tb.addField( new Roo.form.TextField({
27512                 name: '-roo-edit-' + i,
27513                 attrname : i,
27514                 
27515                 width: item.width,
27516                 //allowBlank:true,
27517                 value: '',
27518                 listeners: {
27519                     'change' : function(f, nv, ov) {
27520                         tb.selectedNode.setAttribute(f.attrname, nv);
27521                     }
27522                 }
27523             }));
27524              
27525         }
27526         tb.el.on('click', function(e){
27527             e.preventDefault(); // what does this do?
27528         });
27529         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
27530         tb.el.hide();
27531         tb.name = nm;
27532         // dont need to disable them... as they will get hidden
27533         return tb;
27534          
27535         
27536     },
27537     buildFooter : function()
27538     {
27539         
27540         var fel = this.editor.wrap.createChild();
27541         this.footer = new Roo.Toolbar(fel);
27542         // toolbar has scrolly on left / right?
27543         var footDisp= new Roo.Toolbar.Fill();
27544         var _t = this;
27545         this.footer.add(
27546             {
27547                 text : '&lt;',
27548                 xtype: 'Button',
27549                 handler : function() {
27550                     _t.footDisp.scrollTo('left',0,true)
27551                 }
27552             }
27553         );
27554         this.footer.add( footDisp );
27555         this.footer.add( 
27556             {
27557                 text : '&gt;',
27558                 xtype: 'Button',
27559                 handler : function() {
27560                     // no animation..
27561                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
27562                 }
27563             }
27564         );
27565         var fel = Roo.get(footDisp.el);
27566         fel.addClass('x-editor-context');
27567         this.footDispWrap = fel; 
27568         this.footDispWrap.overflow  = 'hidden';
27569         
27570         this.footDisp = fel.createChild();
27571         this.footDispWrap.on('click', this.onContextClick, this)
27572         
27573         
27574     },
27575     onContextClick : function (ev,dom)
27576     {
27577         ev.preventDefault();
27578         var  cn = dom.className;
27579         Roo.log(cn);
27580         if (!cn.match(/x-ed-loc-/)) {
27581             return;
27582         }
27583         var n = cn.split('-').pop();
27584         var ans = this.footerEls;
27585         var sel = ans[n];
27586         
27587          // pick
27588         var range = this.editor.createRange();
27589         
27590         range.selectNodeContents(sel);
27591         //range.selectNode(sel);
27592         
27593         
27594         var selection = this.editor.getSelection();
27595         selection.removeAllRanges();
27596         selection.addRange(range);
27597         
27598         
27599         
27600         this.updateToolbar(null, null, sel);
27601         
27602         
27603     }
27604     
27605     
27606     
27607     
27608     
27609 });
27610
27611
27612
27613
27614
27615 /*
27616  * Based on:
27617  * Ext JS Library 1.1.1
27618  * Copyright(c) 2006-2007, Ext JS, LLC.
27619  *
27620  * Originally Released Under LGPL - original licence link has changed is not relivant.
27621  *
27622  * Fork - LGPL
27623  * <script type="text/javascript">
27624  */
27625  
27626 /**
27627  * @class Roo.form.BasicForm
27628  * @extends Roo.util.Observable
27629  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
27630  * @constructor
27631  * @param {String/HTMLElement/Roo.Element} el The form element or its id
27632  * @param {Object} config Configuration options
27633  */
27634 Roo.form.BasicForm = function(el, config){
27635     this.allItems = [];
27636     this.childForms = [];
27637     Roo.apply(this, config);
27638     /*
27639      * The Roo.form.Field items in this form.
27640      * @type MixedCollection
27641      */
27642      
27643      
27644     this.items = new Roo.util.MixedCollection(false, function(o){
27645         return o.id || (o.id = Roo.id());
27646     });
27647     this.addEvents({
27648         /**
27649          * @event beforeaction
27650          * Fires before any action is performed. Return false to cancel the action.
27651          * @param {Form} this
27652          * @param {Action} action The action to be performed
27653          */
27654         beforeaction: true,
27655         /**
27656          * @event actionfailed
27657          * Fires when an action fails.
27658          * @param {Form} this
27659          * @param {Action} action The action that failed
27660          */
27661         actionfailed : true,
27662         /**
27663          * @event actioncomplete
27664          * Fires when an action is completed.
27665          * @param {Form} this
27666          * @param {Action} action The action that completed
27667          */
27668         actioncomplete : true
27669     });
27670     if(el){
27671         this.initEl(el);
27672     }
27673     Roo.form.BasicForm.superclass.constructor.call(this);
27674 };
27675
27676 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
27677     /**
27678      * @cfg {String} method
27679      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
27680      */
27681     /**
27682      * @cfg {DataReader} reader
27683      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
27684      * This is optional as there is built-in support for processing JSON.
27685      */
27686     /**
27687      * @cfg {DataReader} errorReader
27688      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
27689      * This is completely optional as there is built-in support for processing JSON.
27690      */
27691     /**
27692      * @cfg {String} url
27693      * The URL to use for form actions if one isn't supplied in the action options.
27694      */
27695     /**
27696      * @cfg {Boolean} fileUpload
27697      * Set to true if this form is a file upload.
27698      */
27699      
27700     /**
27701      * @cfg {Object} baseParams
27702      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
27703      */
27704      /**
27705      
27706     /**
27707      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
27708      */
27709     timeout: 30,
27710
27711     // private
27712     activeAction : null,
27713
27714     /**
27715      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
27716      * or setValues() data instead of when the form was first created.
27717      */
27718     trackResetOnLoad : false,
27719     
27720     
27721     /**
27722      * childForms - used for multi-tab forms
27723      * @type {Array}
27724      */
27725     childForms : false,
27726     
27727     /**
27728      * allItems - full list of fields.
27729      * @type {Array}
27730      */
27731     allItems : false,
27732     
27733     /**
27734      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
27735      * element by passing it or its id or mask the form itself by passing in true.
27736      * @type Mixed
27737      */
27738     waitMsgTarget : false,
27739
27740     // private
27741     initEl : function(el){
27742         this.el = Roo.get(el);
27743         this.id = this.el.id || Roo.id();
27744         this.el.on('submit', this.onSubmit, this);
27745         this.el.addClass('x-form');
27746     },
27747
27748     // private
27749     onSubmit : function(e){
27750         e.stopEvent();
27751     },
27752
27753     /**
27754      * Returns true if client-side validation on the form is successful.
27755      * @return Boolean
27756      */
27757     isValid : function(){
27758         var valid = true;
27759         this.items.each(function(f){
27760            if(!f.validate()){
27761                valid = false;
27762            }
27763         });
27764         return valid;
27765     },
27766
27767     /**
27768      * Returns true if any fields in this form have changed since their original load.
27769      * @return Boolean
27770      */
27771     isDirty : function(){
27772         var dirty = false;
27773         this.items.each(function(f){
27774            if(f.isDirty()){
27775                dirty = true;
27776                return false;
27777            }
27778         });
27779         return dirty;
27780     },
27781
27782     /**
27783      * Performs a predefined action (submit or load) or custom actions you define on this form.
27784      * @param {String} actionName The name of the action type
27785      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
27786      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
27787      * accept other config options):
27788      * <pre>
27789 Property          Type             Description
27790 ----------------  ---------------  ----------------------------------------------------------------------------------
27791 url               String           The url for the action (defaults to the form's url)
27792 method            String           The form method to use (defaults to the form's method, or POST if not defined)
27793 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
27794 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
27795                                    validate the form on the client (defaults to false)
27796      * </pre>
27797      * @return {BasicForm} this
27798      */
27799     doAction : function(action, options){
27800         if(typeof action == 'string'){
27801             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
27802         }
27803         if(this.fireEvent('beforeaction', this, action) !== false){
27804             this.beforeAction(action);
27805             action.run.defer(100, action);
27806         }
27807         return this;
27808     },
27809
27810     /**
27811      * Shortcut to do a submit action.
27812      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
27813      * @return {BasicForm} this
27814      */
27815     submit : function(options){
27816         this.doAction('submit', options);
27817         return this;
27818     },
27819
27820     /**
27821      * Shortcut to do a load action.
27822      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
27823      * @return {BasicForm} this
27824      */
27825     load : function(options){
27826         this.doAction('load', options);
27827         return this;
27828     },
27829
27830     /**
27831      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
27832      * @param {Record} record The record to edit
27833      * @return {BasicForm} this
27834      */
27835     updateRecord : function(record){
27836         record.beginEdit();
27837         var fs = record.fields;
27838         fs.each(function(f){
27839             var field = this.findField(f.name);
27840             if(field){
27841                 record.set(f.name, field.getValue());
27842             }
27843         }, this);
27844         record.endEdit();
27845         return this;
27846     },
27847
27848     /**
27849      * Loads an Roo.data.Record into this form.
27850      * @param {Record} record The record to load
27851      * @return {BasicForm} this
27852      */
27853     loadRecord : function(record){
27854         this.setValues(record.data);
27855         return this;
27856     },
27857
27858     // private
27859     beforeAction : function(action){
27860         var o = action.options;
27861         
27862        
27863         if(this.waitMsgTarget === true){
27864             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
27865         }else if(this.waitMsgTarget){
27866             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
27867             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
27868         }else {
27869             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
27870         }
27871          
27872     },
27873
27874     // private
27875     afterAction : function(action, success){
27876         this.activeAction = null;
27877         var o = action.options;
27878         
27879         if(this.waitMsgTarget === true){
27880             this.el.unmask();
27881         }else if(this.waitMsgTarget){
27882             this.waitMsgTarget.unmask();
27883         }else{
27884             Roo.MessageBox.updateProgress(1);
27885             Roo.MessageBox.hide();
27886         }
27887          
27888         if(success){
27889             if(o.reset){
27890                 this.reset();
27891             }
27892             Roo.callback(o.success, o.scope, [this, action]);
27893             this.fireEvent('actioncomplete', this, action);
27894             
27895         }else{
27896             
27897             // failure condition..
27898             // we have a scenario where updates need confirming.
27899             // eg. if a locking scenario exists..
27900             // we look for { errors : { needs_confirm : true }} in the response.
27901             if (
27902                 (typeof(action.result) != 'undefined')  &&
27903                 (typeof(action.result.errors) != 'undefined')  &&
27904                 (typeof(action.result.errors.needs_confirm) != 'undefined')
27905            ){
27906                 var _t = this;
27907                 Roo.MessageBox.confirm(
27908                     "Change requires confirmation",
27909                     action.result.errorMsg,
27910                     function(r) {
27911                         if (r != 'yes') {
27912                             return;
27913                         }
27914                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
27915                     }
27916                     
27917                 );
27918                 
27919                 
27920                 
27921                 return;
27922             }
27923             
27924             Roo.callback(o.failure, o.scope, [this, action]);
27925             // show an error message if no failed handler is set..
27926             if (!this.hasListener('actionfailed')) {
27927                 Roo.MessageBox.alert("Error",
27928                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
27929                         action.result.errorMsg :
27930                         "Saving Failed, please check your entries or try again"
27931                 );
27932             }
27933             
27934             this.fireEvent('actionfailed', this, action);
27935         }
27936         
27937     },
27938
27939     /**
27940      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
27941      * @param {String} id The value to search for
27942      * @return Field
27943      */
27944     findField : function(id){
27945         var field = this.items.get(id);
27946         if(!field){
27947             this.items.each(function(f){
27948                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
27949                     field = f;
27950                     return false;
27951                 }
27952             });
27953         }
27954         return field || null;
27955     },
27956
27957     /**
27958      * Add a secondary form to this one, 
27959      * Used to provide tabbed forms. One form is primary, with hidden values 
27960      * which mirror the elements from the other forms.
27961      * 
27962      * @param {Roo.form.Form} form to add.
27963      * 
27964      */
27965     addForm : function(form)
27966     {
27967        
27968         if (this.childForms.indexOf(form) > -1) {
27969             // already added..
27970             return;
27971         }
27972         this.childForms.push(form);
27973         var n = '';
27974         Roo.each(form.allItems, function (fe) {
27975             
27976             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
27977             if (this.findField(n)) { // already added..
27978                 return;
27979             }
27980             var add = new Roo.form.Hidden({
27981                 name : n
27982             });
27983             add.render(this.el);
27984             
27985             this.add( add );
27986         }, this);
27987         
27988     },
27989     /**
27990      * Mark fields in this form invalid in bulk.
27991      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
27992      * @return {BasicForm} this
27993      */
27994     markInvalid : function(errors){
27995         if(errors instanceof Array){
27996             for(var i = 0, len = errors.length; i < len; i++){
27997                 var fieldError = errors[i];
27998                 var f = this.findField(fieldError.id);
27999                 if(f){
28000                     f.markInvalid(fieldError.msg);
28001                 }
28002             }
28003         }else{
28004             var field, id;
28005             for(id in errors){
28006                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
28007                     field.markInvalid(errors[id]);
28008                 }
28009             }
28010         }
28011         Roo.each(this.childForms || [], function (f) {
28012             f.markInvalid(errors);
28013         });
28014         
28015         return this;
28016     },
28017
28018     /**
28019      * Set values for fields in this form in bulk.
28020      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
28021      * @return {BasicForm} this
28022      */
28023     setValues : function(values){
28024         if(values instanceof Array){ // array of objects
28025             for(var i = 0, len = values.length; i < len; i++){
28026                 var v = values[i];
28027                 var f = this.findField(v.id);
28028                 if(f){
28029                     f.setValue(v.value);
28030                     if(this.trackResetOnLoad){
28031                         f.originalValue = f.getValue();
28032                     }
28033                 }
28034             }
28035         }else{ // object hash
28036             var field, id;
28037             for(id in values){
28038                 if(typeof values[id] != 'function' && (field = this.findField(id))){
28039                     
28040                     if (field.setFromData && 
28041                         field.valueField && 
28042                         field.displayField &&
28043                         // combos' with local stores can 
28044                         // be queried via setValue()
28045                         // to set their value..
28046                         (field.store && !field.store.isLocal)
28047                         ) {
28048                         // it's a combo
28049                         var sd = { };
28050                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
28051                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
28052                         field.setFromData(sd);
28053                         
28054                     } else {
28055                         field.setValue(values[id]);
28056                     }
28057                     
28058                     
28059                     if(this.trackResetOnLoad){
28060                         field.originalValue = field.getValue();
28061                     }
28062                 }
28063             }
28064         }
28065          
28066         Roo.each(this.childForms || [], function (f) {
28067             f.setValues(values);
28068         });
28069                 
28070         return this;
28071     },
28072
28073     /**
28074      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
28075      * they are returned as an array.
28076      * @param {Boolean} asString
28077      * @return {Object}
28078      */
28079     getValues : function(asString){
28080         if (this.childForms) {
28081             // copy values from the child forms
28082             Roo.each(this.childForms, function (f) {
28083                 this.setValues(f.getValues());
28084             }, this);
28085         }
28086         
28087         
28088         
28089         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
28090         if(asString === true){
28091             return fs;
28092         }
28093         return Roo.urlDecode(fs);
28094     },
28095     
28096     /**
28097      * Returns the fields in this form as an object with key/value pairs. 
28098      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
28099      * @return {Object}
28100      */
28101     getFieldValues : function(with_hidden)
28102     {
28103         if (this.childForms) {
28104             // copy values from the child forms
28105             // should this call getFieldValues - probably not as we do not currently copy
28106             // hidden fields when we generate..
28107             Roo.each(this.childForms, function (f) {
28108                 this.setValues(f.getValues());
28109             }, this);
28110         }
28111         
28112         var ret = {};
28113         this.items.each(function(f){
28114             if (!f.getName()) {
28115                 return;
28116             }
28117             var v = f.getValue();
28118             // not sure if this supported any more..
28119             if ((typeof(v) == 'object') && f.getRawValue) {
28120                 v = f.getRawValue() ; // dates..
28121             }
28122             // combo boxes where name != hiddenName...
28123             if (f.name != f.getName()) {
28124                 ret[f.name] = f.getRawValue();
28125             }
28126             ret[f.getName()] = v;
28127         });
28128         
28129         return ret;
28130     },
28131
28132     /**
28133      * Clears all invalid messages in this form.
28134      * @return {BasicForm} this
28135      */
28136     clearInvalid : function(){
28137         this.items.each(function(f){
28138            f.clearInvalid();
28139         });
28140         
28141         Roo.each(this.childForms || [], function (f) {
28142             f.clearInvalid();
28143         });
28144         
28145         
28146         return this;
28147     },
28148
28149     /**
28150      * Resets this form.
28151      * @return {BasicForm} this
28152      */
28153     reset : function(){
28154         this.items.each(function(f){
28155             f.reset();
28156         });
28157         
28158         Roo.each(this.childForms || [], function (f) {
28159             f.reset();
28160         });
28161        
28162         
28163         return this;
28164     },
28165
28166     /**
28167      * Add Roo.form components to this form.
28168      * @param {Field} field1
28169      * @param {Field} field2 (optional)
28170      * @param {Field} etc (optional)
28171      * @return {BasicForm} this
28172      */
28173     add : function(){
28174         this.items.addAll(Array.prototype.slice.call(arguments, 0));
28175         return this;
28176     },
28177
28178
28179     /**
28180      * Removes a field from the items collection (does NOT remove its markup).
28181      * @param {Field} field
28182      * @return {BasicForm} this
28183      */
28184     remove : function(field){
28185         this.items.remove(field);
28186         return this;
28187     },
28188
28189     /**
28190      * Looks at the fields in this form, checks them for an id attribute,
28191      * and calls applyTo on the existing dom element with that id.
28192      * @return {BasicForm} this
28193      */
28194     render : function(){
28195         this.items.each(function(f){
28196             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
28197                 f.applyTo(f.id);
28198             }
28199         });
28200         return this;
28201     },
28202
28203     /**
28204      * Calls {@link Ext#apply} for all fields in this form with the passed object.
28205      * @param {Object} values
28206      * @return {BasicForm} this
28207      */
28208     applyToFields : function(o){
28209         this.items.each(function(f){
28210            Roo.apply(f, o);
28211         });
28212         return this;
28213     },
28214
28215     /**
28216      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
28217      * @param {Object} values
28218      * @return {BasicForm} this
28219      */
28220     applyIfToFields : function(o){
28221         this.items.each(function(f){
28222            Roo.applyIf(f, o);
28223         });
28224         return this;
28225     }
28226 });
28227
28228 // back compat
28229 Roo.BasicForm = Roo.form.BasicForm;/*
28230  * Based on:
28231  * Ext JS Library 1.1.1
28232  * Copyright(c) 2006-2007, Ext JS, LLC.
28233  *
28234  * Originally Released Under LGPL - original licence link has changed is not relivant.
28235  *
28236  * Fork - LGPL
28237  * <script type="text/javascript">
28238  */
28239
28240 /**
28241  * @class Roo.form.Form
28242  * @extends Roo.form.BasicForm
28243  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
28244  * @constructor
28245  * @param {Object} config Configuration options
28246  */
28247 Roo.form.Form = function(config){
28248     var xitems =  [];
28249     if (config.items) {
28250         xitems = config.items;
28251         delete config.items;
28252     }
28253    
28254     
28255     Roo.form.Form.superclass.constructor.call(this, null, config);
28256     this.url = this.url || this.action;
28257     if(!this.root){
28258         this.root = new Roo.form.Layout(Roo.applyIf({
28259             id: Roo.id()
28260         }, config));
28261     }
28262     this.active = this.root;
28263     /**
28264      * Array of all the buttons that have been added to this form via {@link addButton}
28265      * @type Array
28266      */
28267     this.buttons = [];
28268     this.allItems = [];
28269     this.addEvents({
28270         /**
28271          * @event clientvalidation
28272          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
28273          * @param {Form} this
28274          * @param {Boolean} valid true if the form has passed client-side validation
28275          */
28276         clientvalidation: true,
28277         /**
28278          * @event rendered
28279          * Fires when the form is rendered
28280          * @param {Roo.form.Form} form
28281          */
28282         rendered : true
28283     });
28284     
28285     if (this.progressUrl) {
28286             // push a hidden field onto the list of fields..
28287             this.addxtype( {
28288                     xns: Roo.form, 
28289                     xtype : 'Hidden', 
28290                     name : 'UPLOAD_IDENTIFIER' 
28291             });
28292         }
28293         
28294     
28295     Roo.each(xitems, this.addxtype, this);
28296     
28297     
28298     
28299 };
28300
28301 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
28302     /**
28303      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
28304      */
28305     /**
28306      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
28307      */
28308     /**
28309      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
28310      */
28311     buttonAlign:'center',
28312
28313     /**
28314      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
28315      */
28316     minButtonWidth:75,
28317
28318     /**
28319      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
28320      * This property cascades to child containers if not set.
28321      */
28322     labelAlign:'left',
28323
28324     /**
28325      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
28326      * fires a looping event with that state. This is required to bind buttons to the valid
28327      * state using the config value formBind:true on the button.
28328      */
28329     monitorValid : false,
28330
28331     /**
28332      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
28333      */
28334     monitorPoll : 200,
28335     
28336     /**
28337      * @cfg {String} progressUrl - Url to return progress data 
28338      */
28339     
28340     progressUrl : false,
28341   
28342     /**
28343      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
28344      * fields are added and the column is closed. If no fields are passed the column remains open
28345      * until end() is called.
28346      * @param {Object} config The config to pass to the column
28347      * @param {Field} field1 (optional)
28348      * @param {Field} field2 (optional)
28349      * @param {Field} etc (optional)
28350      * @return Column The column container object
28351      */
28352     column : function(c){
28353         var col = new Roo.form.Column(c);
28354         this.start(col);
28355         if(arguments.length > 1){ // duplicate code required because of Opera
28356             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28357             this.end();
28358         }
28359         return col;
28360     },
28361
28362     /**
28363      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
28364      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
28365      * until end() is called.
28366      * @param {Object} config The config to pass to the fieldset
28367      * @param {Field} field1 (optional)
28368      * @param {Field} field2 (optional)
28369      * @param {Field} etc (optional)
28370      * @return FieldSet The fieldset container object
28371      */
28372     fieldset : function(c){
28373         var fs = new Roo.form.FieldSet(c);
28374         this.start(fs);
28375         if(arguments.length > 1){ // duplicate code required because of Opera
28376             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28377             this.end();
28378         }
28379         return fs;
28380     },
28381
28382     /**
28383      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
28384      * fields are added and the container is closed. If no fields are passed the container remains open
28385      * until end() is called.
28386      * @param {Object} config The config to pass to the Layout
28387      * @param {Field} field1 (optional)
28388      * @param {Field} field2 (optional)
28389      * @param {Field} etc (optional)
28390      * @return Layout The container object
28391      */
28392     container : function(c){
28393         var l = new Roo.form.Layout(c);
28394         this.start(l);
28395         if(arguments.length > 1){ // duplicate code required because of Opera
28396             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28397             this.end();
28398         }
28399         return l;
28400     },
28401
28402     /**
28403      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
28404      * @param {Object} container A Roo.form.Layout or subclass of Layout
28405      * @return {Form} this
28406      */
28407     start : function(c){
28408         // cascade label info
28409         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
28410         this.active.stack.push(c);
28411         c.ownerCt = this.active;
28412         this.active = c;
28413         return this;
28414     },
28415
28416     /**
28417      * Closes the current open container
28418      * @return {Form} this
28419      */
28420     end : function(){
28421         if(this.active == this.root){
28422             return this;
28423         }
28424         this.active = this.active.ownerCt;
28425         return this;
28426     },
28427
28428     /**
28429      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
28430      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
28431      * as the label of the field.
28432      * @param {Field} field1
28433      * @param {Field} field2 (optional)
28434      * @param {Field} etc. (optional)
28435      * @return {Form} this
28436      */
28437     add : function(){
28438         this.active.stack.push.apply(this.active.stack, arguments);
28439         this.allItems.push.apply(this.allItems,arguments);
28440         var r = [];
28441         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
28442             if(a[i].isFormField){
28443                 r.push(a[i]);
28444             }
28445         }
28446         if(r.length > 0){
28447             Roo.form.Form.superclass.add.apply(this, r);
28448         }
28449         return this;
28450     },
28451     
28452
28453     
28454     
28455     
28456      /**
28457      * Find any element that has been added to a form, using it's ID or name
28458      * This can include framesets, columns etc. along with regular fields..
28459      * @param {String} id - id or name to find.
28460      
28461      * @return {Element} e - or false if nothing found.
28462      */
28463     findbyId : function(id)
28464     {
28465         var ret = false;
28466         if (!id) {
28467             return ret;
28468         }
28469         Roo.each(this.allItems, function(f){
28470             if (f.id == id || f.name == id ){
28471                 ret = f;
28472                 return false;
28473             }
28474         });
28475         return ret;
28476     },
28477
28478     
28479     
28480     /**
28481      * Render this form into the passed container. This should only be called once!
28482      * @param {String/HTMLElement/Element} container The element this component should be rendered into
28483      * @return {Form} this
28484      */
28485     render : function(ct)
28486     {
28487         
28488         
28489         
28490         ct = Roo.get(ct);
28491         var o = this.autoCreate || {
28492             tag: 'form',
28493             method : this.method || 'POST',
28494             id : this.id || Roo.id()
28495         };
28496         this.initEl(ct.createChild(o));
28497
28498         this.root.render(this.el);
28499         
28500        
28501              
28502         this.items.each(function(f){
28503             f.render('x-form-el-'+f.id);
28504         });
28505
28506         if(this.buttons.length > 0){
28507             // tables are required to maintain order and for correct IE layout
28508             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
28509                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
28510                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
28511             }}, null, true);
28512             var tr = tb.getElementsByTagName('tr')[0];
28513             for(var i = 0, len = this.buttons.length; i < len; i++) {
28514                 var b = this.buttons[i];
28515                 var td = document.createElement('td');
28516                 td.className = 'x-form-btn-td';
28517                 b.render(tr.appendChild(td));
28518             }
28519         }
28520         if(this.monitorValid){ // initialize after render
28521             this.startMonitoring();
28522         }
28523         this.fireEvent('rendered', this);
28524         return this;
28525     },
28526
28527     /**
28528      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
28529      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
28530      * object or a valid Roo.DomHelper element config
28531      * @param {Function} handler The function called when the button is clicked
28532      * @param {Object} scope (optional) The scope of the handler function
28533      * @return {Roo.Button}
28534      */
28535     addButton : function(config, handler, scope){
28536         var bc = {
28537             handler: handler,
28538             scope: scope,
28539             minWidth: this.minButtonWidth,
28540             hideParent:true
28541         };
28542         if(typeof config == "string"){
28543             bc.text = config;
28544         }else{
28545             Roo.apply(bc, config);
28546         }
28547         var btn = new Roo.Button(null, bc);
28548         this.buttons.push(btn);
28549         return btn;
28550     },
28551
28552      /**
28553      * Adds a series of form elements (using the xtype property as the factory method.
28554      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
28555      * @param {Object} config 
28556      */
28557     
28558     addxtype : function()
28559     {
28560         var ar = Array.prototype.slice.call(arguments, 0);
28561         var ret = false;
28562         for(var i = 0; i < ar.length; i++) {
28563             if (!ar[i]) {
28564                 continue; // skip -- if this happends something invalid got sent, we 
28565                 // should ignore it, as basically that interface element will not show up
28566                 // and that should be pretty obvious!!
28567             }
28568             
28569             if (Roo.form[ar[i].xtype]) {
28570                 ar[i].form = this;
28571                 var fe = Roo.factory(ar[i], Roo.form);
28572                 if (!ret) {
28573                     ret = fe;
28574                 }
28575                 fe.form = this;
28576                 if (fe.store) {
28577                     fe.store.form = this;
28578                 }
28579                 if (fe.isLayout) {  
28580                          
28581                     this.start(fe);
28582                     this.allItems.push(fe);
28583                     if (fe.items && fe.addxtype) {
28584                         fe.addxtype.apply(fe, fe.items);
28585                         delete fe.items;
28586                     }
28587                      this.end();
28588                     continue;
28589                 }
28590                 
28591                 
28592                  
28593                 this.add(fe);
28594               //  console.log('adding ' + ar[i].xtype);
28595             }
28596             if (ar[i].xtype == 'Button') {  
28597                 //console.log('adding button');
28598                 //console.log(ar[i]);
28599                 this.addButton(ar[i]);
28600                 this.allItems.push(fe);
28601                 continue;
28602             }
28603             
28604             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
28605                 alert('end is not supported on xtype any more, use items');
28606             //    this.end();
28607             //    //console.log('adding end');
28608             }
28609             
28610         }
28611         return ret;
28612     },
28613     
28614     /**
28615      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
28616      * option "monitorValid"
28617      */
28618     startMonitoring : function(){
28619         if(!this.bound){
28620             this.bound = true;
28621             Roo.TaskMgr.start({
28622                 run : this.bindHandler,
28623                 interval : this.monitorPoll || 200,
28624                 scope: this
28625             });
28626         }
28627     },
28628
28629     /**
28630      * Stops monitoring of the valid state of this form
28631      */
28632     stopMonitoring : function(){
28633         this.bound = false;
28634     },
28635
28636     // private
28637     bindHandler : function(){
28638         if(!this.bound){
28639             return false; // stops binding
28640         }
28641         var valid = true;
28642         this.items.each(function(f){
28643             if(!f.isValid(true)){
28644                 valid = false;
28645                 return false;
28646             }
28647         });
28648         for(var i = 0, len = this.buttons.length; i < len; i++){
28649             var btn = this.buttons[i];
28650             if(btn.formBind === true && btn.disabled === valid){
28651                 btn.setDisabled(!valid);
28652             }
28653         }
28654         this.fireEvent('clientvalidation', this, valid);
28655     }
28656     
28657     
28658     
28659     
28660     
28661     
28662     
28663     
28664 });
28665
28666
28667 // back compat
28668 Roo.Form = Roo.form.Form;
28669 /*
28670  * Based on:
28671  * Ext JS Library 1.1.1
28672  * Copyright(c) 2006-2007, Ext JS, LLC.
28673  *
28674  * Originally Released Under LGPL - original licence link has changed is not relivant.
28675  *
28676  * Fork - LGPL
28677  * <script type="text/javascript">
28678  */
28679  
28680  /**
28681  * @class Roo.form.Action
28682  * Internal Class used to handle form actions
28683  * @constructor
28684  * @param {Roo.form.BasicForm} el The form element or its id
28685  * @param {Object} config Configuration options
28686  */
28687  
28688  
28689 // define the action interface
28690 Roo.form.Action = function(form, options){
28691     this.form = form;
28692     this.options = options || {};
28693 };
28694 /**
28695  * Client Validation Failed
28696  * @const 
28697  */
28698 Roo.form.Action.CLIENT_INVALID = 'client';
28699 /**
28700  * Server Validation Failed
28701  * @const 
28702  */
28703  Roo.form.Action.SERVER_INVALID = 'server';
28704  /**
28705  * Connect to Server Failed
28706  * @const 
28707  */
28708 Roo.form.Action.CONNECT_FAILURE = 'connect';
28709 /**
28710  * Reading Data from Server Failed
28711  * @const 
28712  */
28713 Roo.form.Action.LOAD_FAILURE = 'load';
28714
28715 Roo.form.Action.prototype = {
28716     type : 'default',
28717     failureType : undefined,
28718     response : undefined,
28719     result : undefined,
28720
28721     // interface method
28722     run : function(options){
28723
28724     },
28725
28726     // interface method
28727     success : function(response){
28728
28729     },
28730
28731     // interface method
28732     handleResponse : function(response){
28733
28734     },
28735
28736     // default connection failure
28737     failure : function(response){
28738         
28739         this.response = response;
28740         this.failureType = Roo.form.Action.CONNECT_FAILURE;
28741         this.form.afterAction(this, false);
28742     },
28743
28744     processResponse : function(response){
28745         this.response = response;
28746         if(!response.responseText){
28747             return true;
28748         }
28749         this.result = this.handleResponse(response);
28750         return this.result;
28751     },
28752
28753     // utility functions used internally
28754     getUrl : function(appendParams){
28755         var url = this.options.url || this.form.url || this.form.el.dom.action;
28756         if(appendParams){
28757             var p = this.getParams();
28758             if(p){
28759                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
28760             }
28761         }
28762         return url;
28763     },
28764
28765     getMethod : function(){
28766         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
28767     },
28768
28769     getParams : function(){
28770         var bp = this.form.baseParams;
28771         var p = this.options.params;
28772         if(p){
28773             if(typeof p == "object"){
28774                 p = Roo.urlEncode(Roo.applyIf(p, bp));
28775             }else if(typeof p == 'string' && bp){
28776                 p += '&' + Roo.urlEncode(bp);
28777             }
28778         }else if(bp){
28779             p = Roo.urlEncode(bp);
28780         }
28781         return p;
28782     },
28783
28784     createCallback : function(){
28785         return {
28786             success: this.success,
28787             failure: this.failure,
28788             scope: this,
28789             timeout: (this.form.timeout*1000),
28790             upload: this.form.fileUpload ? this.success : undefined
28791         };
28792     }
28793 };
28794
28795 Roo.form.Action.Submit = function(form, options){
28796     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
28797 };
28798
28799 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
28800     type : 'submit',
28801
28802     haveProgress : false,
28803     uploadComplete : false,
28804     
28805     // uploadProgress indicator.
28806     uploadProgress : function()
28807     {
28808         if (!this.form.progressUrl) {
28809             return;
28810         }
28811         
28812         if (!this.haveProgress) {
28813             Roo.MessageBox.progress("Uploading", "Uploading");
28814         }
28815         if (this.uploadComplete) {
28816            Roo.MessageBox.hide();
28817            return;
28818         }
28819         
28820         this.haveProgress = true;
28821    
28822         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
28823         
28824         var c = new Roo.data.Connection();
28825         c.request({
28826             url : this.form.progressUrl,
28827             params: {
28828                 id : uid
28829             },
28830             method: 'GET',
28831             success : function(req){
28832                //console.log(data);
28833                 var rdata = false;
28834                 var edata;
28835                 try  {
28836                    rdata = Roo.decode(req.responseText)
28837                 } catch (e) {
28838                     Roo.log("Invalid data from server..");
28839                     Roo.log(edata);
28840                     return;
28841                 }
28842                 if (!rdata || !rdata.success) {
28843                     Roo.log(rdata);
28844                     Roo.MessageBox.alert(Roo.encode(rdata));
28845                     return;
28846                 }
28847                 var data = rdata.data;
28848                 
28849                 if (this.uploadComplete) {
28850                    Roo.MessageBox.hide();
28851                    return;
28852                 }
28853                    
28854                 if (data){
28855                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
28856                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
28857                     );
28858                 }
28859                 this.uploadProgress.defer(2000,this);
28860             },
28861        
28862             failure: function(data) {
28863                 Roo.log('progress url failed ');
28864                 Roo.log(data);
28865             },
28866             scope : this
28867         });
28868            
28869     },
28870     
28871     
28872     run : function()
28873     {
28874         // run get Values on the form, so it syncs any secondary forms.
28875         this.form.getValues();
28876         
28877         var o = this.options;
28878         var method = this.getMethod();
28879         var isPost = method == 'POST';
28880         if(o.clientValidation === false || this.form.isValid()){
28881             
28882             if (this.form.progressUrl) {
28883                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
28884                     (new Date() * 1) + '' + Math.random());
28885                     
28886             } 
28887             
28888             
28889             Roo.Ajax.request(Roo.apply(this.createCallback(), {
28890                 form:this.form.el.dom,
28891                 url:this.getUrl(!isPost),
28892                 method: method,
28893                 params:isPost ? this.getParams() : null,
28894                 isUpload: this.form.fileUpload
28895             }));
28896             
28897             this.uploadProgress();
28898
28899         }else if (o.clientValidation !== false){ // client validation failed
28900             this.failureType = Roo.form.Action.CLIENT_INVALID;
28901             this.form.afterAction(this, false);
28902         }
28903     },
28904
28905     success : function(response)
28906     {
28907         this.uploadComplete= true;
28908         if (this.haveProgress) {
28909             Roo.MessageBox.hide();
28910         }
28911         
28912         
28913         var result = this.processResponse(response);
28914         if(result === true || result.success){
28915             this.form.afterAction(this, true);
28916             return;
28917         }
28918         if(result.errors){
28919             this.form.markInvalid(result.errors);
28920             this.failureType = Roo.form.Action.SERVER_INVALID;
28921         }
28922         this.form.afterAction(this, false);
28923     },
28924     failure : function(response)
28925     {
28926         this.uploadComplete= true;
28927         if (this.haveProgress) {
28928             Roo.MessageBox.hide();
28929         }
28930         
28931         this.response = response;
28932         this.failureType = Roo.form.Action.CONNECT_FAILURE;
28933         this.form.afterAction(this, false);
28934     },
28935     
28936     handleResponse : function(response){
28937         if(this.form.errorReader){
28938             var rs = this.form.errorReader.read(response);
28939             var errors = [];
28940             if(rs.records){
28941                 for(var i = 0, len = rs.records.length; i < len; i++) {
28942                     var r = rs.records[i];
28943                     errors[i] = r.data;
28944                 }
28945             }
28946             if(errors.length < 1){
28947                 errors = null;
28948             }
28949             return {
28950                 success : rs.success,
28951                 errors : errors
28952             };
28953         }
28954         var ret = false;
28955         try {
28956             ret = Roo.decode(response.responseText);
28957         } catch (e) {
28958             ret = {
28959                 success: false,
28960                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
28961                 errors : []
28962             };
28963         }
28964         return ret;
28965         
28966     }
28967 });
28968
28969
28970 Roo.form.Action.Load = function(form, options){
28971     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
28972     this.reader = this.form.reader;
28973 };
28974
28975 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
28976     type : 'load',
28977
28978     run : function(){
28979         
28980         Roo.Ajax.request(Roo.apply(
28981                 this.createCallback(), {
28982                     method:this.getMethod(),
28983                     url:this.getUrl(false),
28984                     params:this.getParams()
28985         }));
28986     },
28987
28988     success : function(response){
28989         
28990         var result = this.processResponse(response);
28991         if(result === true || !result.success || !result.data){
28992             this.failureType = Roo.form.Action.LOAD_FAILURE;
28993             this.form.afterAction(this, false);
28994             return;
28995         }
28996         this.form.clearInvalid();
28997         this.form.setValues(result.data);
28998         this.form.afterAction(this, true);
28999     },
29000
29001     handleResponse : function(response){
29002         if(this.form.reader){
29003             var rs = this.form.reader.read(response);
29004             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
29005             return {
29006                 success : rs.success,
29007                 data : data
29008             };
29009         }
29010         return Roo.decode(response.responseText);
29011     }
29012 });
29013
29014 Roo.form.Action.ACTION_TYPES = {
29015     'load' : Roo.form.Action.Load,
29016     'submit' : Roo.form.Action.Submit
29017 };/*
29018  * Based on:
29019  * Ext JS Library 1.1.1
29020  * Copyright(c) 2006-2007, Ext JS, LLC.
29021  *
29022  * Originally Released Under LGPL - original licence link has changed is not relivant.
29023  *
29024  * Fork - LGPL
29025  * <script type="text/javascript">
29026  */
29027  
29028 /**
29029  * @class Roo.form.Layout
29030  * @extends Roo.Component
29031  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
29032  * @constructor
29033  * @param {Object} config Configuration options
29034  */
29035 Roo.form.Layout = function(config){
29036     var xitems = [];
29037     if (config.items) {
29038         xitems = config.items;
29039         delete config.items;
29040     }
29041     Roo.form.Layout.superclass.constructor.call(this, config);
29042     this.stack = [];
29043     Roo.each(xitems, this.addxtype, this);
29044      
29045 };
29046
29047 Roo.extend(Roo.form.Layout, Roo.Component, {
29048     /**
29049      * @cfg {String/Object} autoCreate
29050      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
29051      */
29052     /**
29053      * @cfg {String/Object/Function} style
29054      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
29055      * a function which returns such a specification.
29056      */
29057     /**
29058      * @cfg {String} labelAlign
29059      * Valid values are "left," "top" and "right" (defaults to "left")
29060      */
29061     /**
29062      * @cfg {Number} labelWidth
29063      * Fixed width in pixels of all field labels (defaults to undefined)
29064      */
29065     /**
29066      * @cfg {Boolean} clear
29067      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
29068      */
29069     clear : true,
29070     /**
29071      * @cfg {String} labelSeparator
29072      * The separator to use after field labels (defaults to ':')
29073      */
29074     labelSeparator : ':',
29075     /**
29076      * @cfg {Boolean} hideLabels
29077      * True to suppress the display of field labels in this layout (defaults to false)
29078      */
29079     hideLabels : false,
29080
29081     // private
29082     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
29083     
29084     isLayout : true,
29085     
29086     // private
29087     onRender : function(ct, position){
29088         if(this.el){ // from markup
29089             this.el = Roo.get(this.el);
29090         }else {  // generate
29091             var cfg = this.getAutoCreate();
29092             this.el = ct.createChild(cfg, position);
29093         }
29094         if(this.style){
29095             this.el.applyStyles(this.style);
29096         }
29097         if(this.labelAlign){
29098             this.el.addClass('x-form-label-'+this.labelAlign);
29099         }
29100         if(this.hideLabels){
29101             this.labelStyle = "display:none";
29102             this.elementStyle = "padding-left:0;";
29103         }else{
29104             if(typeof this.labelWidth == 'number'){
29105                 this.labelStyle = "width:"+this.labelWidth+"px;";
29106                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
29107             }
29108             if(this.labelAlign == 'top'){
29109                 this.labelStyle = "width:auto;";
29110                 this.elementStyle = "padding-left:0;";
29111             }
29112         }
29113         var stack = this.stack;
29114         var slen = stack.length;
29115         if(slen > 0){
29116             if(!this.fieldTpl){
29117                 var t = new Roo.Template(
29118                     '<div class="x-form-item {5}">',
29119                         '<label for="{0}" style="{2}">{1}{4}</label>',
29120                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29121                         '</div>',
29122                     '</div><div class="x-form-clear-left"></div>'
29123                 );
29124                 t.disableFormats = true;
29125                 t.compile();
29126                 Roo.form.Layout.prototype.fieldTpl = t;
29127             }
29128             for(var i = 0; i < slen; i++) {
29129                 if(stack[i].isFormField){
29130                     this.renderField(stack[i]);
29131                 }else{
29132                     this.renderComponent(stack[i]);
29133                 }
29134             }
29135         }
29136         if(this.clear){
29137             this.el.createChild({cls:'x-form-clear'});
29138         }
29139     },
29140
29141     // private
29142     renderField : function(f){
29143         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
29144                f.id, //0
29145                f.fieldLabel, //1
29146                f.labelStyle||this.labelStyle||'', //2
29147                this.elementStyle||'', //3
29148                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
29149                f.itemCls||this.itemCls||''  //5
29150        ], true).getPrevSibling());
29151     },
29152
29153     // private
29154     renderComponent : function(c){
29155         c.render(c.isLayout ? this.el : this.el.createChild());    
29156     },
29157     /**
29158      * Adds a object form elements (using the xtype property as the factory method.)
29159      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
29160      * @param {Object} config 
29161      */
29162     addxtype : function(o)
29163     {
29164         // create the lement.
29165         o.form = this.form;
29166         var fe = Roo.factory(o, Roo.form);
29167         this.form.allItems.push(fe);
29168         this.stack.push(fe);
29169         
29170         if (fe.isFormField) {
29171             this.form.items.add(fe);
29172         }
29173          
29174         return fe;
29175     }
29176 });
29177
29178 /**
29179  * @class Roo.form.Column
29180  * @extends Roo.form.Layout
29181  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
29182  * @constructor
29183  * @param {Object} config Configuration options
29184  */
29185 Roo.form.Column = function(config){
29186     Roo.form.Column.superclass.constructor.call(this, config);
29187 };
29188
29189 Roo.extend(Roo.form.Column, Roo.form.Layout, {
29190     /**
29191      * @cfg {Number/String} width
29192      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29193      */
29194     /**
29195      * @cfg {String/Object} autoCreate
29196      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
29197      */
29198
29199     // private
29200     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
29201
29202     // private
29203     onRender : function(ct, position){
29204         Roo.form.Column.superclass.onRender.call(this, ct, position);
29205         if(this.width){
29206             this.el.setWidth(this.width);
29207         }
29208     }
29209 });
29210
29211
29212 /**
29213  * @class Roo.form.Row
29214  * @extends Roo.form.Layout
29215  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
29216  * @constructor
29217  * @param {Object} config Configuration options
29218  */
29219
29220  
29221 Roo.form.Row = function(config){
29222     Roo.form.Row.superclass.constructor.call(this, config);
29223 };
29224  
29225 Roo.extend(Roo.form.Row, Roo.form.Layout, {
29226       /**
29227      * @cfg {Number/String} width
29228      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29229      */
29230     /**
29231      * @cfg {Number/String} height
29232      * The fixed height of the column in pixels or CSS value (defaults to "auto")
29233      */
29234     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
29235     
29236     padWidth : 20,
29237     // private
29238     onRender : function(ct, position){
29239         //console.log('row render');
29240         if(!this.rowTpl){
29241             var t = new Roo.Template(
29242                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
29243                     '<label for="{0}" style="{2}">{1}{4}</label>',
29244                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29245                     '</div>',
29246                 '</div>'
29247             );
29248             t.disableFormats = true;
29249             t.compile();
29250             Roo.form.Layout.prototype.rowTpl = t;
29251         }
29252         this.fieldTpl = this.rowTpl;
29253         
29254         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
29255         var labelWidth = 100;
29256         
29257         if ((this.labelAlign != 'top')) {
29258             if (typeof this.labelWidth == 'number') {
29259                 labelWidth = this.labelWidth
29260             }
29261             this.padWidth =  20 + labelWidth;
29262             
29263         }
29264         
29265         Roo.form.Column.superclass.onRender.call(this, ct, position);
29266         if(this.width){
29267             this.el.setWidth(this.width);
29268         }
29269         if(this.height){
29270             this.el.setHeight(this.height);
29271         }
29272     },
29273     
29274     // private
29275     renderField : function(f){
29276         f.fieldEl = this.fieldTpl.append(this.el, [
29277                f.id, f.fieldLabel,
29278                f.labelStyle||this.labelStyle||'',
29279                this.elementStyle||'',
29280                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
29281                f.itemCls||this.itemCls||'',
29282                f.width ? f.width + this.padWidth : 160 + this.padWidth
29283        ],true);
29284     }
29285 });
29286  
29287
29288 /**
29289  * @class Roo.form.FieldSet
29290  * @extends Roo.form.Layout
29291  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
29292  * @constructor
29293  * @param {Object} config Configuration options
29294  */
29295 Roo.form.FieldSet = function(config){
29296     Roo.form.FieldSet.superclass.constructor.call(this, config);
29297 };
29298
29299 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
29300     /**
29301      * @cfg {String} legend
29302      * The text to display as the legend for the FieldSet (defaults to '')
29303      */
29304     /**
29305      * @cfg {String/Object} autoCreate
29306      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
29307      */
29308
29309     // private
29310     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
29311
29312     // private
29313     onRender : function(ct, position){
29314         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
29315         if(this.legend){
29316             this.setLegend(this.legend);
29317         }
29318     },
29319
29320     // private
29321     setLegend : function(text){
29322         if(this.rendered){
29323             this.el.child('legend').update(text);
29324         }
29325     }
29326 });/*
29327  * Based on:
29328  * Ext JS Library 1.1.1
29329  * Copyright(c) 2006-2007, Ext JS, LLC.
29330  *
29331  * Originally Released Under LGPL - original licence link has changed is not relivant.
29332  *
29333  * Fork - LGPL
29334  * <script type="text/javascript">
29335  */
29336 /**
29337  * @class Roo.form.VTypes
29338  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
29339  * @singleton
29340  */
29341 Roo.form.VTypes = function(){
29342     // closure these in so they are only created once.
29343     var alpha = /^[a-zA-Z_]+$/;
29344     var alphanum = /^[a-zA-Z0-9_]+$/;
29345     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
29346     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
29347
29348     // All these messages and functions are configurable
29349     return {
29350         /**
29351          * The function used to validate email addresses
29352          * @param {String} value The email address
29353          */
29354         'email' : function(v){
29355             return email.test(v);
29356         },
29357         /**
29358          * The error text to display when the email validation function returns false
29359          * @type String
29360          */
29361         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
29362         /**
29363          * The keystroke filter mask to be applied on email input
29364          * @type RegExp
29365          */
29366         'emailMask' : /[a-z0-9_\.\-@]/i,
29367
29368         /**
29369          * The function used to validate URLs
29370          * @param {String} value The URL
29371          */
29372         'url' : function(v){
29373             return url.test(v);
29374         },
29375         /**
29376          * The error text to display when the url validation function returns false
29377          * @type String
29378          */
29379         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
29380         
29381         /**
29382          * The function used to validate alpha values
29383          * @param {String} value The value
29384          */
29385         'alpha' : function(v){
29386             return alpha.test(v);
29387         },
29388         /**
29389          * The error text to display when the alpha validation function returns false
29390          * @type String
29391          */
29392         'alphaText' : 'This field should only contain letters and _',
29393         /**
29394          * The keystroke filter mask to be applied on alpha input
29395          * @type RegExp
29396          */
29397         'alphaMask' : /[a-z_]/i,
29398
29399         /**
29400          * The function used to validate alphanumeric values
29401          * @param {String} value The value
29402          */
29403         'alphanum' : function(v){
29404             return alphanum.test(v);
29405         },
29406         /**
29407          * The error text to display when the alphanumeric validation function returns false
29408          * @type String
29409          */
29410         'alphanumText' : 'This field should only contain letters, numbers and _',
29411         /**
29412          * The keystroke filter mask to be applied on alphanumeric input
29413          * @type RegExp
29414          */
29415         'alphanumMask' : /[a-z0-9_]/i
29416     };
29417 }();//<script type="text/javascript">
29418
29419 /**
29420  * @class Roo.form.FCKeditor
29421  * @extends Roo.form.TextArea
29422  * Wrapper around the FCKEditor http://www.fckeditor.net
29423  * @constructor
29424  * Creates a new FCKeditor
29425  * @param {Object} config Configuration options
29426  */
29427 Roo.form.FCKeditor = function(config){
29428     Roo.form.FCKeditor.superclass.constructor.call(this, config);
29429     this.addEvents({
29430          /**
29431          * @event editorinit
29432          * Fired when the editor is initialized - you can add extra handlers here..
29433          * @param {FCKeditor} this
29434          * @param {Object} the FCK object.
29435          */
29436         editorinit : true
29437     });
29438     
29439     
29440 };
29441 Roo.form.FCKeditor.editors = { };
29442 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
29443 {
29444     //defaultAutoCreate : {
29445     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
29446     //},
29447     // private
29448     /**
29449      * @cfg {Object} fck options - see fck manual for details.
29450      */
29451     fckconfig : false,
29452     
29453     /**
29454      * @cfg {Object} fck toolbar set (Basic or Default)
29455      */
29456     toolbarSet : 'Basic',
29457     /**
29458      * @cfg {Object} fck BasePath
29459      */ 
29460     basePath : '/fckeditor/',
29461     
29462     
29463     frame : false,
29464     
29465     value : '',
29466     
29467    
29468     onRender : function(ct, position)
29469     {
29470         if(!this.el){
29471             this.defaultAutoCreate = {
29472                 tag: "textarea",
29473                 style:"width:300px;height:60px;",
29474                 autocomplete: "off"
29475             };
29476         }
29477         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
29478         /*
29479         if(this.grow){
29480             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
29481             if(this.preventScrollbars){
29482                 this.el.setStyle("overflow", "hidden");
29483             }
29484             this.el.setHeight(this.growMin);
29485         }
29486         */
29487         //console.log('onrender' + this.getId() );
29488         Roo.form.FCKeditor.editors[this.getId()] = this;
29489          
29490
29491         this.replaceTextarea() ;
29492         
29493     },
29494     
29495     getEditor : function() {
29496         return this.fckEditor;
29497     },
29498     /**
29499      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
29500      * @param {Mixed} value The value to set
29501      */
29502     
29503     
29504     setValue : function(value)
29505     {
29506         //console.log('setValue: ' + value);
29507         
29508         if(typeof(value) == 'undefined') { // not sure why this is happending...
29509             return;
29510         }
29511         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29512         
29513         //if(!this.el || !this.getEditor()) {
29514         //    this.value = value;
29515             //this.setValue.defer(100,this,[value]);    
29516         //    return;
29517         //} 
29518         
29519         if(!this.getEditor()) {
29520             return;
29521         }
29522         
29523         this.getEditor().SetData(value);
29524         
29525         //
29526
29527     },
29528
29529     /**
29530      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
29531      * @return {Mixed} value The field value
29532      */
29533     getValue : function()
29534     {
29535         
29536         if (this.frame && this.frame.dom.style.display == 'none') {
29537             return Roo.form.FCKeditor.superclass.getValue.call(this);
29538         }
29539         
29540         if(!this.el || !this.getEditor()) {
29541            
29542            // this.getValue.defer(100,this); 
29543             return this.value;
29544         }
29545        
29546         
29547         var value=this.getEditor().GetData();
29548         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29549         return Roo.form.FCKeditor.superclass.getValue.call(this);
29550         
29551
29552     },
29553
29554     /**
29555      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
29556      * @return {Mixed} value The field value
29557      */
29558     getRawValue : function()
29559     {
29560         if (this.frame && this.frame.dom.style.display == 'none') {
29561             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29562         }
29563         
29564         if(!this.el || !this.getEditor()) {
29565             //this.getRawValue.defer(100,this); 
29566             return this.value;
29567             return;
29568         }
29569         
29570         
29571         
29572         var value=this.getEditor().GetData();
29573         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
29574         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29575          
29576     },
29577     
29578     setSize : function(w,h) {
29579         
29580         
29581         
29582         //if (this.frame && this.frame.dom.style.display == 'none') {
29583         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
29584         //    return;
29585         //}
29586         //if(!this.el || !this.getEditor()) {
29587         //    this.setSize.defer(100,this, [w,h]); 
29588         //    return;
29589         //}
29590         
29591         
29592         
29593         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
29594         
29595         this.frame.dom.setAttribute('width', w);
29596         this.frame.dom.setAttribute('height', h);
29597         this.frame.setSize(w,h);
29598         
29599     },
29600     
29601     toggleSourceEdit : function(value) {
29602         
29603       
29604          
29605         this.el.dom.style.display = value ? '' : 'none';
29606         this.frame.dom.style.display = value ?  'none' : '';
29607         
29608     },
29609     
29610     
29611     focus: function(tag)
29612     {
29613         if (this.frame.dom.style.display == 'none') {
29614             return Roo.form.FCKeditor.superclass.focus.call(this);
29615         }
29616         if(!this.el || !this.getEditor()) {
29617             this.focus.defer(100,this, [tag]); 
29618             return;
29619         }
29620         
29621         
29622         
29623         
29624         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
29625         this.getEditor().Focus();
29626         if (tgs.length) {
29627             if (!this.getEditor().Selection.GetSelection()) {
29628                 this.focus.defer(100,this, [tag]); 
29629                 return;
29630             }
29631             
29632             
29633             var r = this.getEditor().EditorDocument.createRange();
29634             r.setStart(tgs[0],0);
29635             r.setEnd(tgs[0],0);
29636             this.getEditor().Selection.GetSelection().removeAllRanges();
29637             this.getEditor().Selection.GetSelection().addRange(r);
29638             this.getEditor().Focus();
29639         }
29640         
29641     },
29642     
29643     
29644     
29645     replaceTextarea : function()
29646     {
29647         if ( document.getElementById( this.getId() + '___Frame' ) )
29648             return ;
29649         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
29650         //{
29651             // We must check the elements firstly using the Id and then the name.
29652         var oTextarea = document.getElementById( this.getId() );
29653         
29654         var colElementsByName = document.getElementsByName( this.getId() ) ;
29655          
29656         oTextarea.style.display = 'none' ;
29657
29658         if ( oTextarea.tabIndex ) {            
29659             this.TabIndex = oTextarea.tabIndex ;
29660         }
29661         
29662         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
29663         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
29664         this.frame = Roo.get(this.getId() + '___Frame')
29665     },
29666     
29667     _getConfigHtml : function()
29668     {
29669         var sConfig = '' ;
29670
29671         for ( var o in this.fckconfig ) {
29672             sConfig += sConfig.length > 0  ? '&amp;' : '';
29673             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
29674         }
29675
29676         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
29677     },
29678     
29679     
29680     _getIFrameHtml : function()
29681     {
29682         var sFile = 'fckeditor.html' ;
29683         /* no idea what this is about..
29684         try
29685         {
29686             if ( (/fcksource=true/i).test( window.top.location.search ) )
29687                 sFile = 'fckeditor.original.html' ;
29688         }
29689         catch (e) { 
29690         */
29691
29692         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
29693         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
29694         
29695         
29696         var html = '<iframe id="' + this.getId() +
29697             '___Frame" src="' + sLink +
29698             '" width="' + this.width +
29699             '" height="' + this.height + '"' +
29700             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
29701             ' frameborder="0" scrolling="no"></iframe>' ;
29702
29703         return html ;
29704     },
29705     
29706     _insertHtmlBefore : function( html, element )
29707     {
29708         if ( element.insertAdjacentHTML )       {
29709             // IE
29710             element.insertAdjacentHTML( 'beforeBegin', html ) ;
29711         } else { // Gecko
29712             var oRange = document.createRange() ;
29713             oRange.setStartBefore( element ) ;
29714             var oFragment = oRange.createContextualFragment( html );
29715             element.parentNode.insertBefore( oFragment, element ) ;
29716         }
29717     }
29718     
29719     
29720   
29721     
29722     
29723     
29724     
29725
29726 });
29727
29728 //Roo.reg('fckeditor', Roo.form.FCKeditor);
29729
29730 function FCKeditor_OnComplete(editorInstance){
29731     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
29732     f.fckEditor = editorInstance;
29733     //console.log("loaded");
29734     f.fireEvent('editorinit', f, editorInstance);
29735
29736   
29737
29738  
29739
29740
29741
29742
29743
29744
29745
29746
29747
29748
29749
29750
29751
29752
29753
29754 //<script type="text/javascript">
29755 /**
29756  * @class Roo.form.GridField
29757  * @extends Roo.form.Field
29758  * Embed a grid (or editable grid into a form)
29759  * STATUS ALPHA
29760  * 
29761  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
29762  * it needs 
29763  * xgrid.store = Roo.data.Store
29764  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
29765  * xgrid.store.reader = Roo.data.JsonReader 
29766  * 
29767  * 
29768  * @constructor
29769  * Creates a new GridField
29770  * @param {Object} config Configuration options
29771  */
29772 Roo.form.GridField = function(config){
29773     Roo.form.GridField.superclass.constructor.call(this, config);
29774      
29775 };
29776
29777 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
29778     /**
29779      * @cfg {Number} width  - used to restrict width of grid..
29780      */
29781     width : 100,
29782     /**
29783      * @cfg {Number} height - used to restrict height of grid..
29784      */
29785     height : 50,
29786      /**
29787      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
29788          * 
29789          *}
29790      */
29791     xgrid : false, 
29792     /**
29793      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
29794      * {tag: "input", type: "checkbox", autocomplete: "off"})
29795      */
29796    // defaultAutoCreate : { tag: 'div' },
29797     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
29798     /**
29799      * @cfg {String} addTitle Text to include for adding a title.
29800      */
29801     addTitle : false,
29802     //
29803     onResize : function(){
29804         Roo.form.Field.superclass.onResize.apply(this, arguments);
29805     },
29806
29807     initEvents : function(){
29808         // Roo.form.Checkbox.superclass.initEvents.call(this);
29809         // has no events...
29810        
29811     },
29812
29813
29814     getResizeEl : function(){
29815         return this.wrap;
29816     },
29817
29818     getPositionEl : function(){
29819         return this.wrap;
29820     },
29821
29822     // private
29823     onRender : function(ct, position){
29824         
29825         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
29826         var style = this.style;
29827         delete this.style;
29828         
29829         Roo.form.GridField.superclass.onRender.call(this, ct, position);
29830         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
29831         this.viewEl = this.wrap.createChild({ tag: 'div' });
29832         if (style) {
29833             this.viewEl.applyStyles(style);
29834         }
29835         if (this.width) {
29836             this.viewEl.setWidth(this.width);
29837         }
29838         if (this.height) {
29839             this.viewEl.setHeight(this.height);
29840         }
29841         //if(this.inputValue !== undefined){
29842         //this.setValue(this.value);
29843         
29844         
29845         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
29846         
29847         
29848         this.grid.render();
29849         this.grid.getDataSource().on('remove', this.refreshValue, this);
29850         this.grid.getDataSource().on('update', this.refreshValue, this);
29851         this.grid.on('afteredit', this.refreshValue, this);
29852  
29853     },
29854      
29855     
29856     /**
29857      * Sets the value of the item. 
29858      * @param {String} either an object  or a string..
29859      */
29860     setValue : function(v){
29861         //this.value = v;
29862         v = v || []; // empty set..
29863         // this does not seem smart - it really only affects memoryproxy grids..
29864         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
29865             var ds = this.grid.getDataSource();
29866             // assumes a json reader..
29867             var data = {}
29868             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
29869             ds.loadData( data);
29870         }
29871         // clear selection so it does not get stale.
29872         if (this.grid.sm) { 
29873             this.grid.sm.clearSelections();
29874         }
29875         
29876         Roo.form.GridField.superclass.setValue.call(this, v);
29877         this.refreshValue();
29878         // should load data in the grid really....
29879     },
29880     
29881     // private
29882     refreshValue: function() {
29883          var val = [];
29884         this.grid.getDataSource().each(function(r) {
29885             val.push(r.data);
29886         });
29887         this.el.dom.value = Roo.encode(val);
29888     }
29889     
29890      
29891     
29892     
29893 });/*
29894  * Based on:
29895  * Ext JS Library 1.1.1
29896  * Copyright(c) 2006-2007, Ext JS, LLC.
29897  *
29898  * Originally Released Under LGPL - original licence link has changed is not relivant.
29899  *
29900  * Fork - LGPL
29901  * <script type="text/javascript">
29902  */
29903 /**
29904  * @class Roo.form.DisplayField
29905  * @extends Roo.form.Field
29906  * A generic Field to display non-editable data.
29907  * @constructor
29908  * Creates a new Display Field item.
29909  * @param {Object} config Configuration options
29910  */
29911 Roo.form.DisplayField = function(config){
29912     Roo.form.DisplayField.superclass.constructor.call(this, config);
29913     
29914 };
29915
29916 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
29917     inputType:      'hidden',
29918     allowBlank:     true,
29919     readOnly:         true,
29920     
29921  
29922     /**
29923      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
29924      */
29925     focusClass : undefined,
29926     /**
29927      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
29928      */
29929     fieldClass: 'x-form-field',
29930     
29931      /**
29932      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
29933      */
29934     valueRenderer: undefined,
29935     
29936     width: 100,
29937     /**
29938      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
29939      * {tag: "input", type: "checkbox", autocomplete: "off"})
29940      */
29941      
29942  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
29943
29944     onResize : function(){
29945         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
29946         
29947     },
29948
29949     initEvents : function(){
29950         // Roo.form.Checkbox.superclass.initEvents.call(this);
29951         // has no events...
29952        
29953     },
29954
29955
29956     getResizeEl : function(){
29957         return this.wrap;
29958     },
29959
29960     getPositionEl : function(){
29961         return this.wrap;
29962     },
29963
29964     // private
29965     onRender : function(ct, position){
29966         
29967         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
29968         //if(this.inputValue !== undefined){
29969         this.wrap = this.el.wrap();
29970         
29971         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
29972         
29973         if (this.bodyStyle) {
29974             this.viewEl.applyStyles(this.bodyStyle);
29975         }
29976         //this.viewEl.setStyle('padding', '2px');
29977         
29978         this.setValue(this.value);
29979         
29980     },
29981 /*
29982     // private
29983     initValue : Roo.emptyFn,
29984
29985   */
29986
29987         // private
29988     onClick : function(){
29989         
29990     },
29991
29992     /**
29993      * Sets the checked state of the checkbox.
29994      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
29995      */
29996     setValue : function(v){
29997         this.value = v;
29998         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
29999         // this might be called before we have a dom element..
30000         if (!this.viewEl) {
30001             return;
30002         }
30003         this.viewEl.dom.innerHTML = html;
30004         Roo.form.DisplayField.superclass.setValue.call(this, v);
30005
30006     }
30007 });/*
30008  * 
30009  * Licence- LGPL
30010  * 
30011  */
30012
30013 /**
30014  * @class Roo.form.DayPicker
30015  * @extends Roo.form.Field
30016  * A Day picker show [M] [T] [W] ....
30017  * @constructor
30018  * Creates a new Day Picker
30019  * @param {Object} config Configuration options
30020  */
30021 Roo.form.DayPicker= function(config){
30022     Roo.form.DayPicker.superclass.constructor.call(this, config);
30023      
30024 };
30025
30026 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
30027     /**
30028      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30029      */
30030     focusClass : undefined,
30031     /**
30032      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30033      */
30034     fieldClass: "x-form-field",
30035    
30036     /**
30037      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30038      * {tag: "input", type: "checkbox", autocomplete: "off"})
30039      */
30040     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
30041     
30042    
30043     actionMode : 'viewEl', 
30044     //
30045     // private
30046  
30047     inputType : 'hidden',
30048     
30049      
30050     inputElement: false, // real input element?
30051     basedOn: false, // ????
30052     
30053     isFormField: true, // not sure where this is needed!!!!
30054
30055     onResize : function(){
30056         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
30057         if(!this.boxLabel){
30058             this.el.alignTo(this.wrap, 'c-c');
30059         }
30060     },
30061
30062     initEvents : function(){
30063         Roo.form.Checkbox.superclass.initEvents.call(this);
30064         this.el.on("click", this.onClick,  this);
30065         this.el.on("change", this.onClick,  this);
30066     },
30067
30068
30069     getResizeEl : function(){
30070         return this.wrap;
30071     },
30072
30073     getPositionEl : function(){
30074         return this.wrap;
30075     },
30076
30077     
30078     // private
30079     onRender : function(ct, position){
30080         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
30081        
30082         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
30083         
30084         var r1 = '<table><tr>';
30085         var r2 = '<tr class="x-form-daypick-icons">';
30086         for (var i=0; i < 7; i++) {
30087             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
30088             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
30089         }
30090         
30091         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
30092         viewEl.select('img').on('click', this.onClick, this);
30093         this.viewEl = viewEl;   
30094         
30095         
30096         // this will not work on Chrome!!!
30097         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
30098         this.el.on('propertychange', this.setFromHidden,  this);  //ie
30099         
30100         
30101           
30102
30103     },
30104
30105     // private
30106     initValue : Roo.emptyFn,
30107
30108     /**
30109      * Returns the checked state of the checkbox.
30110      * @return {Boolean} True if checked, else false
30111      */
30112     getValue : function(){
30113         return this.el.dom.value;
30114         
30115     },
30116
30117         // private
30118     onClick : function(e){ 
30119         //this.setChecked(!this.checked);
30120         Roo.get(e.target).toggleClass('x-menu-item-checked');
30121         this.refreshValue();
30122         //if(this.el.dom.checked != this.checked){
30123         //    this.setValue(this.el.dom.checked);
30124        // }
30125     },
30126     
30127     // private
30128     refreshValue : function()
30129     {
30130         var val = '';
30131         this.viewEl.select('img',true).each(function(e,i,n)  {
30132             val += e.is(".x-menu-item-checked") ? String(n) : '';
30133         });
30134         this.setValue(val, true);
30135     },
30136
30137     /**
30138      * Sets the checked state of the checkbox.
30139      * On is always based on a string comparison between inputValue and the param.
30140      * @param {Boolean/String} value - the value to set 
30141      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
30142      */
30143     setValue : function(v,suppressEvent){
30144         if (!this.el.dom) {
30145             return;
30146         }
30147         var old = this.el.dom.value ;
30148         this.el.dom.value = v;
30149         if (suppressEvent) {
30150             return ;
30151         }
30152          
30153         // update display..
30154         this.viewEl.select('img',true).each(function(e,i,n)  {
30155             
30156             var on = e.is(".x-menu-item-checked");
30157             var newv = v.indexOf(String(n)) > -1;
30158             if (on != newv) {
30159                 e.toggleClass('x-menu-item-checked');
30160             }
30161             
30162         });
30163         
30164         
30165         this.fireEvent('change', this, v, old);
30166         
30167         
30168     },
30169    
30170     // handle setting of hidden value by some other method!!?!?
30171     setFromHidden: function()
30172     {
30173         if(!this.el){
30174             return;
30175         }
30176         //console.log("SET FROM HIDDEN");
30177         //alert('setFrom hidden');
30178         this.setValue(this.el.dom.value);
30179     },
30180     
30181     onDestroy : function()
30182     {
30183         if(this.viewEl){
30184             Roo.get(this.viewEl).remove();
30185         }
30186          
30187         Roo.form.DayPicker.superclass.onDestroy.call(this);
30188     }
30189
30190 });/*
30191  * RooJS Library 1.1.1
30192  * Copyright(c) 2008-2011  Alan Knowles
30193  *
30194  * License - LGPL
30195  */
30196  
30197
30198 /**
30199  * @class Roo.form.ComboCheck
30200  * @extends Roo.form.ComboBox
30201  * A combobox for multiple select items.
30202  *
30203  * FIXME - could do with a reset button..
30204  * 
30205  * @constructor
30206  * Create a new ComboCheck
30207  * @param {Object} config Configuration options
30208  */
30209 Roo.form.ComboCheck = function(config){
30210     Roo.form.ComboCheck.superclass.constructor.call(this, config);
30211     // should verify some data...
30212     // like
30213     // hiddenName = required..
30214     // displayField = required
30215     // valudField == required
30216     var req= [ 'hiddenName', 'displayField', 'valueField' ];
30217     var _t = this;
30218     Roo.each(req, function(e) {
30219         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
30220             throw "Roo.form.ComboCheck : missing value for: " + e;
30221         }
30222     });
30223     
30224     
30225 };
30226
30227 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
30228      
30229      
30230     editable : false,
30231      
30232     selectedClass: 'x-menu-item-checked', 
30233     
30234     // private
30235     onRender : function(ct, position){
30236         var _t = this;
30237         
30238         
30239         
30240         if(!this.tpl){
30241             var cls = 'x-combo-list';
30242
30243             
30244             this.tpl =  new Roo.Template({
30245                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
30246                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
30247                    '<span>{' + this.displayField + '}</span>' +
30248                     '</div>' 
30249                 
30250             });
30251         }
30252  
30253         
30254         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
30255         this.view.singleSelect = false;
30256         this.view.multiSelect = true;
30257         this.view.toggleSelect = true;
30258         this.pageTb.add(new Roo.Toolbar.Fill(), {
30259             
30260             text: 'Done',
30261             handler: function()
30262             {
30263                 _t.collapse();
30264             }
30265         });
30266     },
30267     
30268     onViewOver : function(e, t){
30269         // do nothing...
30270         return;
30271         
30272     },
30273     
30274     onViewClick : function(doFocus,index){
30275         return;
30276         
30277     },
30278     select: function () {
30279         //Roo.log("SELECT CALLED");
30280     },
30281      
30282     selectByValue : function(xv, scrollIntoView){
30283         var ar = this.getValueArray();
30284         var sels = [];
30285         
30286         Roo.each(ar, function(v) {
30287             if(v === undefined || v === null){
30288                 return;
30289             }
30290             var r = this.findRecord(this.valueField, v);
30291             if(r){
30292                 sels.push(this.store.indexOf(r))
30293                 
30294             }
30295         },this);
30296         this.view.select(sels);
30297         return false;
30298     },
30299     
30300     
30301     
30302     onSelect : function(record, index){
30303        // Roo.log("onselect Called");
30304        // this is only called by the clear button now..
30305         this.view.clearSelections();
30306         this.setValue('[]');
30307         if (this.value != this.valueBefore) {
30308             this.fireEvent('change', this, this.value, this.valueBefore);
30309         }
30310     },
30311     getValueArray : function()
30312     {
30313         var ar = [] ;
30314         
30315         try {
30316             //Roo.log(this.value);
30317             if (typeof(this.value) == 'undefined') {
30318                 return [];
30319             }
30320             var ar = Roo.decode(this.value);
30321             return  ar instanceof Array ? ar : []; //?? valid?
30322             
30323         } catch(e) {
30324             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
30325             return [];
30326         }
30327          
30328     },
30329     expand : function ()
30330     {
30331         Roo.form.ComboCheck.superclass.expand.call(this);
30332         this.valueBefore = this.value;
30333         
30334
30335     },
30336     
30337     collapse : function(){
30338         Roo.form.ComboCheck.superclass.collapse.call(this);
30339         var sl = this.view.getSelectedIndexes();
30340         var st = this.store;
30341         var nv = [];
30342         var tv = [];
30343         var r;
30344         Roo.each(sl, function(i) {
30345             r = st.getAt(i);
30346             nv.push(r.get(this.valueField));
30347         },this);
30348         this.setValue(Roo.encode(nv));
30349         if (this.value != this.valueBefore) {
30350
30351             this.fireEvent('change', this, this.value, this.valueBefore);
30352         }
30353         
30354     },
30355     
30356     setValue : function(v){
30357         // Roo.log(v);
30358         this.value = v;
30359         
30360         var vals = this.getValueArray();
30361         var tv = [];
30362         Roo.each(vals, function(k) {
30363             var r = this.findRecord(this.valueField, k);
30364             if(r){
30365                 tv.push(r.data[this.displayField]);
30366             }else if(this.valueNotFoundText !== undefined){
30367                 tv.push( this.valueNotFoundText );
30368             }
30369         },this);
30370        // Roo.log(tv);
30371         
30372         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
30373         this.hiddenField.value = v;
30374         this.value = v;
30375     }
30376     
30377 });//<script type="text/javasscript">
30378  
30379
30380 /**
30381  * @class Roo.DDView
30382  * A DnD enabled version of Roo.View.
30383  * @param {Element/String} container The Element in which to create the View.
30384  * @param {String} tpl The template string used to create the markup for each element of the View
30385  * @param {Object} config The configuration properties. These include all the config options of
30386  * {@link Roo.View} plus some specific to this class.<br>
30387  * <p>
30388  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
30389  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
30390  * <p>
30391  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
30392 .x-view-drag-insert-above {
30393         border-top:1px dotted #3366cc;
30394 }
30395 .x-view-drag-insert-below {
30396         border-bottom:1px dotted #3366cc;
30397 }
30398 </code></pre>
30399  * 
30400  */
30401  
30402 Roo.DDView = function(container, tpl, config) {
30403     Roo.DDView.superclass.constructor.apply(this, arguments);
30404     this.getEl().setStyle("outline", "0px none");
30405     this.getEl().unselectable();
30406     if (this.dragGroup) {
30407                 this.setDraggable(this.dragGroup.split(","));
30408     }
30409     if (this.dropGroup) {
30410                 this.setDroppable(this.dropGroup.split(","));
30411     }
30412     if (this.deletable) {
30413         this.setDeletable();
30414     }
30415     this.isDirtyFlag = false;
30416         this.addEvents({
30417                 "drop" : true
30418         });
30419 };
30420
30421 Roo.extend(Roo.DDView, Roo.View, {
30422 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
30423 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
30424 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
30425 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
30426
30427         isFormField: true,
30428
30429         reset: Roo.emptyFn,
30430         
30431         clearInvalid: Roo.form.Field.prototype.clearInvalid,
30432
30433         validate: function() {
30434                 return true;
30435         },
30436         
30437         destroy: function() {
30438                 this.purgeListeners();
30439                 this.getEl.removeAllListeners();
30440                 this.getEl().remove();
30441                 if (this.dragZone) {
30442                         if (this.dragZone.destroy) {
30443                                 this.dragZone.destroy();
30444                         }
30445                 }
30446                 if (this.dropZone) {
30447                         if (this.dropZone.destroy) {
30448                                 this.dropZone.destroy();
30449                         }
30450                 }
30451         },
30452
30453 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
30454         getName: function() {
30455                 return this.name;
30456         },
30457
30458 /**     Loads the View from a JSON string representing the Records to put into the Store. */
30459         setValue: function(v) {
30460                 if (!this.store) {
30461                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
30462                 }
30463                 var data = {};
30464                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
30465                 this.store.proxy = new Roo.data.MemoryProxy(data);
30466                 this.store.load();
30467         },
30468
30469 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
30470         getValue: function() {
30471                 var result = '(';
30472                 this.store.each(function(rec) {
30473                         result += rec.id + ',';
30474                 });
30475                 return result.substr(0, result.length - 1) + ')';
30476         },
30477         
30478         getIds: function() {
30479                 var i = 0, result = new Array(this.store.getCount());
30480                 this.store.each(function(rec) {
30481                         result[i++] = rec.id;
30482                 });
30483                 return result;
30484         },
30485         
30486         isDirty: function() {
30487                 return this.isDirtyFlag;
30488         },
30489
30490 /**
30491  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
30492  *      whole Element becomes the target, and this causes the drop gesture to append.
30493  */
30494     getTargetFromEvent : function(e) {
30495                 var target = e.getTarget();
30496                 while ((target !== null) && (target.parentNode != this.el.dom)) {
30497                 target = target.parentNode;
30498                 }
30499                 if (!target) {
30500                         target = this.el.dom.lastChild || this.el.dom;
30501                 }
30502                 return target;
30503     },
30504
30505 /**
30506  *      Create the drag data which consists of an object which has the property "ddel" as
30507  *      the drag proxy element. 
30508  */
30509     getDragData : function(e) {
30510         var target = this.findItemFromChild(e.getTarget());
30511                 if(target) {
30512                         this.handleSelection(e);
30513                         var selNodes = this.getSelectedNodes();
30514             var dragData = {
30515                 source: this,
30516                 copy: this.copy || (this.allowCopy && e.ctrlKey),
30517                 nodes: selNodes,
30518                 records: []
30519                         };
30520                         var selectedIndices = this.getSelectedIndexes();
30521                         for (var i = 0; i < selectedIndices.length; i++) {
30522                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
30523                         }
30524                         if (selNodes.length == 1) {
30525                                 dragData.ddel = target.cloneNode(true); // the div element
30526                         } else {
30527                                 var div = document.createElement('div'); // create the multi element drag "ghost"
30528                                 div.className = 'multi-proxy';
30529                                 for (var i = 0, len = selNodes.length; i < len; i++) {
30530                                         div.appendChild(selNodes[i].cloneNode(true));
30531                                 }
30532                                 dragData.ddel = div;
30533                         }
30534             //console.log(dragData)
30535             //console.log(dragData.ddel.innerHTML)
30536                         return dragData;
30537                 }
30538         //console.log('nodragData')
30539                 return false;
30540     },
30541     
30542 /**     Specify to which ddGroup items in this DDView may be dragged. */
30543     setDraggable: function(ddGroup) {
30544         if (ddGroup instanceof Array) {
30545                 Roo.each(ddGroup, this.setDraggable, this);
30546                 return;
30547         }
30548         if (this.dragZone) {
30549                 this.dragZone.addToGroup(ddGroup);
30550         } else {
30551                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
30552                                 containerScroll: true,
30553                                 ddGroup: ddGroup 
30554
30555                         });
30556 //                      Draggability implies selection. DragZone's mousedown selects the element.
30557                         if (!this.multiSelect) { this.singleSelect = true; }
30558
30559 //                      Wire the DragZone's handlers up to methods in *this*
30560                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
30561                 }
30562     },
30563
30564 /**     Specify from which ddGroup this DDView accepts drops. */
30565     setDroppable: function(ddGroup) {
30566         if (ddGroup instanceof Array) {
30567                 Roo.each(ddGroup, this.setDroppable, this);
30568                 return;
30569         }
30570         if (this.dropZone) {
30571                 this.dropZone.addToGroup(ddGroup);
30572         } else {
30573                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
30574                                 containerScroll: true,
30575                                 ddGroup: ddGroup
30576                         });
30577
30578 //                      Wire the DropZone's handlers up to methods in *this*
30579                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
30580                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
30581                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
30582                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
30583                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
30584                 }
30585     },
30586
30587 /**     Decide whether to drop above or below a View node. */
30588     getDropPoint : function(e, n, dd){
30589         if (n == this.el.dom) { return "above"; }
30590                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
30591                 var c = t + (b - t) / 2;
30592                 var y = Roo.lib.Event.getPageY(e);
30593                 if(y <= c) {
30594                         return "above";
30595                 }else{
30596                         return "below";
30597                 }
30598     },
30599
30600     onNodeEnter : function(n, dd, e, data){
30601                 return false;
30602     },
30603     
30604     onNodeOver : function(n, dd, e, data){
30605                 var pt = this.getDropPoint(e, n, dd);
30606                 // set the insert point style on the target node
30607                 var dragElClass = this.dropNotAllowed;
30608                 if (pt) {
30609                         var targetElClass;
30610                         if (pt == "above"){
30611                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
30612                                 targetElClass = "x-view-drag-insert-above";
30613                         } else {
30614                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
30615                                 targetElClass = "x-view-drag-insert-below";
30616                         }
30617                         if (this.lastInsertClass != targetElClass){
30618                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
30619                                 this.lastInsertClass = targetElClass;
30620                         }
30621                 }
30622                 return dragElClass;
30623         },
30624
30625     onNodeOut : function(n, dd, e, data){
30626                 this.removeDropIndicators(n);
30627     },
30628
30629     onNodeDrop : function(n, dd, e, data){
30630         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
30631                 return false;
30632         }
30633         var pt = this.getDropPoint(e, n, dd);
30634                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
30635                 if (pt == "below") { insertAt++; }
30636                 for (var i = 0; i < data.records.length; i++) {
30637                         var r = data.records[i];
30638                         var dup = this.store.getById(r.id);
30639                         if (dup && (dd != this.dragZone)) {
30640                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
30641                         } else {
30642                                 if (data.copy) {
30643                                         this.store.insert(insertAt++, r.copy());
30644                                 } else {
30645                                         data.source.isDirtyFlag = true;
30646                                         r.store.remove(r);
30647                                         this.store.insert(insertAt++, r);
30648                                 }
30649                                 this.isDirtyFlag = true;
30650                         }
30651                 }
30652                 this.dragZone.cachedTarget = null;
30653                 return true;
30654     },
30655
30656     removeDropIndicators : function(n){
30657                 if(n){
30658                         Roo.fly(n).removeClass([
30659                                 "x-view-drag-insert-above",
30660                                 "x-view-drag-insert-below"]);
30661                         this.lastInsertClass = "_noclass";
30662                 }
30663     },
30664
30665 /**
30666  *      Utility method. Add a delete option to the DDView's context menu.
30667  *      @param {String} imageUrl The URL of the "delete" icon image.
30668  */
30669         setDeletable: function(imageUrl) {
30670                 if (!this.singleSelect && !this.multiSelect) {
30671                         this.singleSelect = true;
30672                 }
30673                 var c = this.getContextMenu();
30674                 this.contextMenu.on("itemclick", function(item) {
30675                         switch (item.id) {
30676                                 case "delete":
30677                                         this.remove(this.getSelectedIndexes());
30678                                         break;
30679                         }
30680                 }, this);
30681                 this.contextMenu.add({
30682                         icon: imageUrl,
30683                         id: "delete",
30684                         text: 'Delete'
30685                 });
30686         },
30687         
30688 /**     Return the context menu for this DDView. */
30689         getContextMenu: function() {
30690                 if (!this.contextMenu) {
30691 //                      Create the View's context menu
30692                         this.contextMenu = new Roo.menu.Menu({
30693                                 id: this.id + "-contextmenu"
30694                         });
30695                         this.el.on("contextmenu", this.showContextMenu, this);
30696                 }
30697                 return this.contextMenu;
30698         },
30699         
30700         disableContextMenu: function() {
30701                 if (this.contextMenu) {
30702                         this.el.un("contextmenu", this.showContextMenu, this);
30703                 }
30704         },
30705
30706         showContextMenu: function(e, item) {
30707         item = this.findItemFromChild(e.getTarget());
30708                 if (item) {
30709                         e.stopEvent();
30710                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
30711                         this.contextMenu.showAt(e.getXY());
30712             }
30713     },
30714
30715 /**
30716  *      Remove {@link Roo.data.Record}s at the specified indices.
30717  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
30718  */
30719     remove: function(selectedIndices) {
30720                 selectedIndices = [].concat(selectedIndices);
30721                 for (var i = 0; i < selectedIndices.length; i++) {
30722                         var rec = this.store.getAt(selectedIndices[i]);
30723                         this.store.remove(rec);
30724                 }
30725     },
30726
30727 /**
30728  *      Double click fires the event, but also, if this is draggable, and there is only one other
30729  *      related DropZone, it transfers the selected node.
30730  */
30731     onDblClick : function(e){
30732         var item = this.findItemFromChild(e.getTarget());
30733         if(item){
30734             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
30735                 return false;
30736             }
30737             if (this.dragGroup) {
30738                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
30739                     while (targets.indexOf(this.dropZone) > -1) {
30740                             targets.remove(this.dropZone);
30741                                 }
30742                     if (targets.length == 1) {
30743                                         this.dragZone.cachedTarget = null;
30744                         var el = Roo.get(targets[0].getEl());
30745                         var box = el.getBox(true);
30746                         targets[0].onNodeDrop(el.dom, {
30747                                 target: el.dom,
30748                                 xy: [box.x, box.y + box.height - 1]
30749                         }, null, this.getDragData(e));
30750                     }
30751                 }
30752         }
30753     },
30754     
30755     handleSelection: function(e) {
30756                 this.dragZone.cachedTarget = null;
30757         var item = this.findItemFromChild(e.getTarget());
30758         if (!item) {
30759                 this.clearSelections(true);
30760                 return;
30761         }
30762                 if (item && (this.multiSelect || this.singleSelect)){
30763                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
30764                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
30765                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
30766                                 this.unselect(item);
30767                         } else {
30768                                 this.select(item, this.multiSelect && e.ctrlKey);
30769                                 this.lastSelection = item;
30770                         }
30771                 }
30772     },
30773
30774     onItemClick : function(item, index, e){
30775                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
30776                         return false;
30777                 }
30778                 return true;
30779     },
30780
30781     unselect : function(nodeInfo, suppressEvent){
30782                 var node = this.getNode(nodeInfo);
30783                 if(node && this.isSelected(node)){
30784                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
30785                                 Roo.fly(node).removeClass(this.selectedClass);
30786                                 this.selections.remove(node);
30787                                 if(!suppressEvent){
30788                                         this.fireEvent("selectionchange", this, this.selections);
30789                                 }
30790                         }
30791                 }
30792     }
30793 });
30794 /*
30795  * Based on:
30796  * Ext JS Library 1.1.1
30797  * Copyright(c) 2006-2007, Ext JS, LLC.
30798  *
30799  * Originally Released Under LGPL - original licence link has changed is not relivant.
30800  *
30801  * Fork - LGPL
30802  * <script type="text/javascript">
30803  */
30804  
30805 /**
30806  * @class Roo.LayoutManager
30807  * @extends Roo.util.Observable
30808  * Base class for layout managers.
30809  */
30810 Roo.LayoutManager = function(container, config){
30811     Roo.LayoutManager.superclass.constructor.call(this);
30812     this.el = Roo.get(container);
30813     // ie scrollbar fix
30814     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
30815         document.body.scroll = "no";
30816     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
30817         this.el.position('relative');
30818     }
30819     this.id = this.el.id;
30820     this.el.addClass("x-layout-container");
30821     /** false to disable window resize monitoring @type Boolean */
30822     this.monitorWindowResize = true;
30823     this.regions = {};
30824     this.addEvents({
30825         /**
30826          * @event layout
30827          * Fires when a layout is performed. 
30828          * @param {Roo.LayoutManager} this
30829          */
30830         "layout" : true,
30831         /**
30832          * @event regionresized
30833          * Fires when the user resizes a region. 
30834          * @param {Roo.LayoutRegion} region The resized region
30835          * @param {Number} newSize The new size (width for east/west, height for north/south)
30836          */
30837         "regionresized" : true,
30838         /**
30839          * @event regioncollapsed
30840          * Fires when a region is collapsed. 
30841          * @param {Roo.LayoutRegion} region The collapsed region
30842          */
30843         "regioncollapsed" : true,
30844         /**
30845          * @event regionexpanded
30846          * Fires when a region is expanded.  
30847          * @param {Roo.LayoutRegion} region The expanded region
30848          */
30849         "regionexpanded" : true
30850     });
30851     this.updating = false;
30852     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
30853 };
30854
30855 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
30856     /**
30857      * Returns true if this layout is currently being updated
30858      * @return {Boolean}
30859      */
30860     isUpdating : function(){
30861         return this.updating; 
30862     },
30863     
30864     /**
30865      * Suspend the LayoutManager from doing auto-layouts while
30866      * making multiple add or remove calls
30867      */
30868     beginUpdate : function(){
30869         this.updating = true;    
30870     },
30871     
30872     /**
30873      * Restore auto-layouts and optionally disable the manager from performing a layout
30874      * @param {Boolean} noLayout true to disable a layout update 
30875      */
30876     endUpdate : function(noLayout){
30877         this.updating = false;
30878         if(!noLayout){
30879             this.layout();
30880         }    
30881     },
30882     
30883     layout: function(){
30884         
30885     },
30886     
30887     onRegionResized : function(region, newSize){
30888         this.fireEvent("regionresized", region, newSize);
30889         this.layout();
30890     },
30891     
30892     onRegionCollapsed : function(region){
30893         this.fireEvent("regioncollapsed", region);
30894     },
30895     
30896     onRegionExpanded : function(region){
30897         this.fireEvent("regionexpanded", region);
30898     },
30899         
30900     /**
30901      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
30902      * performs box-model adjustments.
30903      * @return {Object} The size as an object {width: (the width), height: (the height)}
30904      */
30905     getViewSize : function(){
30906         var size;
30907         if(this.el.dom != document.body){
30908             size = this.el.getSize();
30909         }else{
30910             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
30911         }
30912         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
30913         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
30914         return size;
30915     },
30916     
30917     /**
30918      * Returns the Element this layout is bound to.
30919      * @return {Roo.Element}
30920      */
30921     getEl : function(){
30922         return this.el;
30923     },
30924     
30925     /**
30926      * Returns the specified region.
30927      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
30928      * @return {Roo.LayoutRegion}
30929      */
30930     getRegion : function(target){
30931         return this.regions[target.toLowerCase()];
30932     },
30933     
30934     onWindowResize : function(){
30935         if(this.monitorWindowResize){
30936             this.layout();
30937         }
30938     }
30939 });/*
30940  * Based on:
30941  * Ext JS Library 1.1.1
30942  * Copyright(c) 2006-2007, Ext JS, LLC.
30943  *
30944  * Originally Released Under LGPL - original licence link has changed is not relivant.
30945  *
30946  * Fork - LGPL
30947  * <script type="text/javascript">
30948  */
30949 /**
30950  * @class Roo.BorderLayout
30951  * @extends Roo.LayoutManager
30952  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
30953  * please see: <br><br>
30954  * <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>
30955  * <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>
30956  * Example:
30957  <pre><code>
30958  var layout = new Roo.BorderLayout(document.body, {
30959     north: {
30960         initialSize: 25,
30961         titlebar: false
30962     },
30963     west: {
30964         split:true,
30965         initialSize: 200,
30966         minSize: 175,
30967         maxSize: 400,
30968         titlebar: true,
30969         collapsible: true
30970     },
30971     east: {
30972         split:true,
30973         initialSize: 202,
30974         minSize: 175,
30975         maxSize: 400,
30976         titlebar: true,
30977         collapsible: true
30978     },
30979     south: {
30980         split:true,
30981         initialSize: 100,
30982         minSize: 100,
30983         maxSize: 200,
30984         titlebar: true,
30985         collapsible: true
30986     },
30987     center: {
30988         titlebar: true,
30989         autoScroll:true,
30990         resizeTabs: true,
30991         minTabWidth: 50,
30992         preferredTabWidth: 150
30993     }
30994 });
30995
30996 // shorthand
30997 var CP = Roo.ContentPanel;
30998
30999 layout.beginUpdate();
31000 layout.add("north", new CP("north", "North"));
31001 layout.add("south", new CP("south", {title: "South", closable: true}));
31002 layout.add("west", new CP("west", {title: "West"}));
31003 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
31004 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
31005 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
31006 layout.getRegion("center").showPanel("center1");
31007 layout.endUpdate();
31008 </code></pre>
31009
31010 <b>The container the layout is rendered into can be either the body element or any other element.
31011 If it is not the body element, the container needs to either be an absolute positioned element,
31012 or you will need to add "position:relative" to the css of the container.  You will also need to specify
31013 the container size if it is not the body element.</b>
31014
31015 * @constructor
31016 * Create a new BorderLayout
31017 * @param {String/HTMLElement/Element} container The container this layout is bound to
31018 * @param {Object} config Configuration options
31019  */
31020 Roo.BorderLayout = function(container, config){
31021     config = config || {};
31022     Roo.BorderLayout.superclass.constructor.call(this, container, config);
31023     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
31024     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
31025         var target = this.factory.validRegions[i];
31026         if(config[target]){
31027             this.addRegion(target, config[target]);
31028         }
31029     }
31030 };
31031
31032 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
31033     /**
31034      * Creates and adds a new region if it doesn't already exist.
31035      * @param {String} target The target region key (north, south, east, west or center).
31036      * @param {Object} config The regions config object
31037      * @return {BorderLayoutRegion} The new region
31038      */
31039     addRegion : function(target, config){
31040         if(!this.regions[target]){
31041             var r = this.factory.create(target, this, config);
31042             this.bindRegion(target, r);
31043         }
31044         return this.regions[target];
31045     },
31046
31047     // private (kinda)
31048     bindRegion : function(name, r){
31049         this.regions[name] = r;
31050         r.on("visibilitychange", this.layout, this);
31051         r.on("paneladded", this.layout, this);
31052         r.on("panelremoved", this.layout, this);
31053         r.on("invalidated", this.layout, this);
31054         r.on("resized", this.onRegionResized, this);
31055         r.on("collapsed", this.onRegionCollapsed, this);
31056         r.on("expanded", this.onRegionExpanded, this);
31057     },
31058
31059     /**
31060      * Performs a layout update.
31061      */
31062     layout : function(){
31063         if(this.updating) return;
31064         var size = this.getViewSize();
31065         var w = size.width;
31066         var h = size.height;
31067         var centerW = w;
31068         var centerH = h;
31069         var centerY = 0;
31070         var centerX = 0;
31071         //var x = 0, y = 0;
31072
31073         var rs = this.regions;
31074         var north = rs["north"];
31075         var south = rs["south"]; 
31076         var west = rs["west"];
31077         var east = rs["east"];
31078         var center = rs["center"];
31079         //if(this.hideOnLayout){ // not supported anymore
31080             //c.el.setStyle("display", "none");
31081         //}
31082         if(north && north.isVisible()){
31083             var b = north.getBox();
31084             var m = north.getMargins();
31085             b.width = w - (m.left+m.right);
31086             b.x = m.left;
31087             b.y = m.top;
31088             centerY = b.height + b.y + m.bottom;
31089             centerH -= centerY;
31090             north.updateBox(this.safeBox(b));
31091         }
31092         if(south && south.isVisible()){
31093             var b = south.getBox();
31094             var m = south.getMargins();
31095             b.width = w - (m.left+m.right);
31096             b.x = m.left;
31097             var totalHeight = (b.height + m.top + m.bottom);
31098             b.y = h - totalHeight + m.top;
31099             centerH -= totalHeight;
31100             south.updateBox(this.safeBox(b));
31101         }
31102         if(west && west.isVisible()){
31103             var b = west.getBox();
31104             var m = west.getMargins();
31105             b.height = centerH - (m.top+m.bottom);
31106             b.x = m.left;
31107             b.y = centerY + m.top;
31108             var totalWidth = (b.width + m.left + m.right);
31109             centerX += totalWidth;
31110             centerW -= totalWidth;
31111             west.updateBox(this.safeBox(b));
31112         }
31113         if(east && east.isVisible()){
31114             var b = east.getBox();
31115             var m = east.getMargins();
31116             b.height = centerH - (m.top+m.bottom);
31117             var totalWidth = (b.width + m.left + m.right);
31118             b.x = w - totalWidth + m.left;
31119             b.y = centerY + m.top;
31120             centerW -= totalWidth;
31121             east.updateBox(this.safeBox(b));
31122         }
31123         if(center){
31124             var m = center.getMargins();
31125             var centerBox = {
31126                 x: centerX + m.left,
31127                 y: centerY + m.top,
31128                 width: centerW - (m.left+m.right),
31129                 height: centerH - (m.top+m.bottom)
31130             };
31131             //if(this.hideOnLayout){
31132                 //center.el.setStyle("display", "block");
31133             //}
31134             center.updateBox(this.safeBox(centerBox));
31135         }
31136         this.el.repaint();
31137         this.fireEvent("layout", this);
31138     },
31139
31140     // private
31141     safeBox : function(box){
31142         box.width = Math.max(0, box.width);
31143         box.height = Math.max(0, box.height);
31144         return box;
31145     },
31146
31147     /**
31148      * Adds a ContentPanel (or subclass) to this layout.
31149      * @param {String} target The target region key (north, south, east, west or center).
31150      * @param {Roo.ContentPanel} panel The panel to add
31151      * @return {Roo.ContentPanel} The added panel
31152      */
31153     add : function(target, panel){
31154          
31155         target = target.toLowerCase();
31156         return this.regions[target].add(panel);
31157     },
31158
31159     /**
31160      * Remove a ContentPanel (or subclass) to this layout.
31161      * @param {String} target The target region key (north, south, east, west or center).
31162      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
31163      * @return {Roo.ContentPanel} The removed panel
31164      */
31165     remove : function(target, panel){
31166         target = target.toLowerCase();
31167         return this.regions[target].remove(panel);
31168     },
31169
31170     /**
31171      * Searches all regions for a panel with the specified id
31172      * @param {String} panelId
31173      * @return {Roo.ContentPanel} The panel or null if it wasn't found
31174      */
31175     findPanel : function(panelId){
31176         var rs = this.regions;
31177         for(var target in rs){
31178             if(typeof rs[target] != "function"){
31179                 var p = rs[target].getPanel(panelId);
31180                 if(p){
31181                     return p;
31182                 }
31183             }
31184         }
31185         return null;
31186     },
31187
31188     /**
31189      * Searches all regions for a panel with the specified id and activates (shows) it.
31190      * @param {String/ContentPanel} panelId The panels id or the panel itself
31191      * @return {Roo.ContentPanel} The shown panel or null
31192      */
31193     showPanel : function(panelId) {
31194       var rs = this.regions;
31195       for(var target in rs){
31196          var r = rs[target];
31197          if(typeof r != "function"){
31198             if(r.hasPanel(panelId)){
31199                return r.showPanel(panelId);
31200             }
31201          }
31202       }
31203       return null;
31204    },
31205
31206    /**
31207      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
31208      * @param {Roo.state.Provider} provider (optional) An alternate state provider
31209      */
31210     restoreState : function(provider){
31211         if(!provider){
31212             provider = Roo.state.Manager;
31213         }
31214         var sm = new Roo.LayoutStateManager();
31215         sm.init(this, provider);
31216     },
31217
31218     /**
31219      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
31220      * object should contain properties for each region to add ContentPanels to, and each property's value should be
31221      * a valid ContentPanel config object.  Example:
31222      * <pre><code>
31223 // Create the main layout
31224 var layout = new Roo.BorderLayout('main-ct', {
31225     west: {
31226         split:true,
31227         minSize: 175,
31228         titlebar: true
31229     },
31230     center: {
31231         title:'Components'
31232     }
31233 }, 'main-ct');
31234
31235 // Create and add multiple ContentPanels at once via configs
31236 layout.batchAdd({
31237    west: {
31238        id: 'source-files',
31239        autoCreate:true,
31240        title:'Ext Source Files',
31241        autoScroll:true,
31242        fitToFrame:true
31243    },
31244    center : {
31245        el: cview,
31246        autoScroll:true,
31247        fitToFrame:true,
31248        toolbar: tb,
31249        resizeEl:'cbody'
31250    }
31251 });
31252 </code></pre>
31253      * @param {Object} regions An object containing ContentPanel configs by region name
31254      */
31255     batchAdd : function(regions){
31256         this.beginUpdate();
31257         for(var rname in regions){
31258             var lr = this.regions[rname];
31259             if(lr){
31260                 this.addTypedPanels(lr, regions[rname]);
31261             }
31262         }
31263         this.endUpdate();
31264     },
31265
31266     // private
31267     addTypedPanels : function(lr, ps){
31268         if(typeof ps == 'string'){
31269             lr.add(new Roo.ContentPanel(ps));
31270         }
31271         else if(ps instanceof Array){
31272             for(var i =0, len = ps.length; i < len; i++){
31273                 this.addTypedPanels(lr, ps[i]);
31274             }
31275         }
31276         else if(!ps.events){ // raw config?
31277             var el = ps.el;
31278             delete ps.el; // prevent conflict
31279             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
31280         }
31281         else {  // panel object assumed!
31282             lr.add(ps);
31283         }
31284     },
31285     /**
31286      * Adds a xtype elements to the layout.
31287      * <pre><code>
31288
31289 layout.addxtype({
31290        xtype : 'ContentPanel',
31291        region: 'west',
31292        items: [ .... ]
31293    }
31294 );
31295
31296 layout.addxtype({
31297         xtype : 'NestedLayoutPanel',
31298         region: 'west',
31299         layout: {
31300            center: { },
31301            west: { }   
31302         },
31303         items : [ ... list of content panels or nested layout panels.. ]
31304    }
31305 );
31306 </code></pre>
31307      * @param {Object} cfg Xtype definition of item to add.
31308      */
31309     addxtype : function(cfg)
31310     {
31311         // basically accepts a pannel...
31312         // can accept a layout region..!?!?
31313         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
31314         
31315         if (!cfg.xtype.match(/Panel$/)) {
31316             return false;
31317         }
31318         var ret = false;
31319         
31320         if (typeof(cfg.region) == 'undefined') {
31321             Roo.log("Failed to add Panel, region was not set");
31322             Roo.log(cfg);
31323             return false;
31324         }
31325         var region = cfg.region;
31326         delete cfg.region;
31327         
31328           
31329         var xitems = [];
31330         if (cfg.items) {
31331             xitems = cfg.items;
31332             delete cfg.items;
31333         }
31334         var nb = false;
31335         
31336         switch(cfg.xtype) 
31337         {
31338             case 'ContentPanel':  // ContentPanel (el, cfg)
31339             case 'ScrollPanel':  // ContentPanel (el, cfg)
31340                 if(cfg.autoCreate) {
31341                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31342                 } else {
31343                     var el = this.el.createChild();
31344                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
31345                 }
31346                 
31347                 this.add(region, ret);
31348                 break;
31349             
31350             
31351             case 'TreePanel': // our new panel!
31352                 cfg.el = this.el.createChild();
31353                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31354                 this.add(region, ret);
31355                 break;
31356             
31357             case 'NestedLayoutPanel': 
31358                 // create a new Layout (which is  a Border Layout...
31359                 var el = this.el.createChild();
31360                 var clayout = cfg.layout;
31361                 delete cfg.layout;
31362                 clayout.items   = clayout.items  || [];
31363                 // replace this exitems with the clayout ones..
31364                 xitems = clayout.items;
31365                  
31366                 
31367                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
31368                     cfg.background = false;
31369                 }
31370                 var layout = new Roo.BorderLayout(el, clayout);
31371                 
31372                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
31373                 //console.log('adding nested layout panel '  + cfg.toSource());
31374                 this.add(region, ret);
31375                 nb = {}; /// find first...
31376                 break;
31377                 
31378             case 'GridPanel': 
31379             
31380                 // needs grid and region
31381                 
31382                 //var el = this.getRegion(region).el.createChild();
31383                 var el = this.el.createChild();
31384                 // create the grid first...
31385                 
31386                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
31387                 delete cfg.grid;
31388                 if (region == 'center' && this.active ) {
31389                     cfg.background = false;
31390                 }
31391                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
31392                 
31393                 this.add(region, ret);
31394                 if (cfg.background) {
31395                     ret.on('activate', function(gp) {
31396                         if (!gp.grid.rendered) {
31397                             gp.grid.render();
31398                         }
31399                     });
31400                 } else {
31401                     grid.render();
31402                 }
31403                 break;
31404            
31405                
31406                 
31407                 
31408             default: 
31409                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
31410                 return null;
31411              // GridPanel (grid, cfg)
31412             
31413         }
31414         this.beginUpdate();
31415         // add children..
31416         var region = '';
31417         var abn = {};
31418         Roo.each(xitems, function(i)  {
31419             region = nb && i.region ? i.region : false;
31420             
31421             var add = ret.addxtype(i);
31422            
31423             if (region) {
31424                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
31425                 if (!i.background) {
31426                     abn[region] = nb[region] ;
31427                 }
31428             }
31429             
31430         });
31431         this.endUpdate();
31432
31433         // make the last non-background panel active..
31434         //if (nb) { Roo.log(abn); }
31435         if (nb) {
31436             
31437             for(var r in abn) {
31438                 region = this.getRegion(r);
31439                 if (region) {
31440                     // tried using nb[r], but it does not work..
31441                      
31442                     region.showPanel(abn[r]);
31443                    
31444                 }
31445             }
31446         }
31447         return ret;
31448         
31449     }
31450 });
31451
31452 /**
31453  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
31454  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
31455  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
31456  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
31457  * <pre><code>
31458 // shorthand
31459 var CP = Roo.ContentPanel;
31460
31461 var layout = Roo.BorderLayout.create({
31462     north: {
31463         initialSize: 25,
31464         titlebar: false,
31465         panels: [new CP("north", "North")]
31466     },
31467     west: {
31468         split:true,
31469         initialSize: 200,
31470         minSize: 175,
31471         maxSize: 400,
31472         titlebar: true,
31473         collapsible: true,
31474         panels: [new CP("west", {title: "West"})]
31475     },
31476     east: {
31477         split:true,
31478         initialSize: 202,
31479         minSize: 175,
31480         maxSize: 400,
31481         titlebar: true,
31482         collapsible: true,
31483         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
31484     },
31485     south: {
31486         split:true,
31487         initialSize: 100,
31488         minSize: 100,
31489         maxSize: 200,
31490         titlebar: true,
31491         collapsible: true,
31492         panels: [new CP("south", {title: "South", closable: true})]
31493     },
31494     center: {
31495         titlebar: true,
31496         autoScroll:true,
31497         resizeTabs: true,
31498         minTabWidth: 50,
31499         preferredTabWidth: 150,
31500         panels: [
31501             new CP("center1", {title: "Close Me", closable: true}),
31502             new CP("center2", {title: "Center Panel", closable: false})
31503         ]
31504     }
31505 }, document.body);
31506
31507 layout.getRegion("center").showPanel("center1");
31508 </code></pre>
31509  * @param config
31510  * @param targetEl
31511  */
31512 Roo.BorderLayout.create = function(config, targetEl){
31513     var layout = new Roo.BorderLayout(targetEl || document.body, config);
31514     layout.beginUpdate();
31515     var regions = Roo.BorderLayout.RegionFactory.validRegions;
31516     for(var j = 0, jlen = regions.length; j < jlen; j++){
31517         var lr = regions[j];
31518         if(layout.regions[lr] && config[lr].panels){
31519             var r = layout.regions[lr];
31520             var ps = config[lr].panels;
31521             layout.addTypedPanels(r, ps);
31522         }
31523     }
31524     layout.endUpdate();
31525     return layout;
31526 };
31527
31528 // private
31529 Roo.BorderLayout.RegionFactory = {
31530     // private
31531     validRegions : ["north","south","east","west","center"],
31532
31533     // private
31534     create : function(target, mgr, config){
31535         target = target.toLowerCase();
31536         if(config.lightweight || config.basic){
31537             return new Roo.BasicLayoutRegion(mgr, config, target);
31538         }
31539         switch(target){
31540             case "north":
31541                 return new Roo.NorthLayoutRegion(mgr, config);
31542             case "south":
31543                 return new Roo.SouthLayoutRegion(mgr, config);
31544             case "east":
31545                 return new Roo.EastLayoutRegion(mgr, config);
31546             case "west":
31547                 return new Roo.WestLayoutRegion(mgr, config);
31548             case "center":
31549                 return new Roo.CenterLayoutRegion(mgr, config);
31550         }
31551         throw 'Layout region "'+target+'" not supported.';
31552     }
31553 };/*
31554  * Based on:
31555  * Ext JS Library 1.1.1
31556  * Copyright(c) 2006-2007, Ext JS, LLC.
31557  *
31558  * Originally Released Under LGPL - original licence link has changed is not relivant.
31559  *
31560  * Fork - LGPL
31561  * <script type="text/javascript">
31562  */
31563  
31564 /**
31565  * @class Roo.BasicLayoutRegion
31566  * @extends Roo.util.Observable
31567  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
31568  * and does not have a titlebar, tabs or any other features. All it does is size and position 
31569  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
31570  */
31571 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
31572     this.mgr = mgr;
31573     this.position  = pos;
31574     this.events = {
31575         /**
31576          * @scope Roo.BasicLayoutRegion
31577          */
31578         
31579         /**
31580          * @event beforeremove
31581          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
31582          * @param {Roo.LayoutRegion} this
31583          * @param {Roo.ContentPanel} panel The panel
31584          * @param {Object} e The cancel event object
31585          */
31586         "beforeremove" : true,
31587         /**
31588          * @event invalidated
31589          * Fires when the layout for this region is changed.
31590          * @param {Roo.LayoutRegion} this
31591          */
31592         "invalidated" : true,
31593         /**
31594          * @event visibilitychange
31595          * Fires when this region is shown or hidden 
31596          * @param {Roo.LayoutRegion} this
31597          * @param {Boolean} visibility true or false
31598          */
31599         "visibilitychange" : true,
31600         /**
31601          * @event paneladded
31602          * Fires when a panel is added. 
31603          * @param {Roo.LayoutRegion} this
31604          * @param {Roo.ContentPanel} panel The panel
31605          */
31606         "paneladded" : true,
31607         /**
31608          * @event panelremoved
31609          * Fires when a panel is removed. 
31610          * @param {Roo.LayoutRegion} this
31611          * @param {Roo.ContentPanel} panel The panel
31612          */
31613         "panelremoved" : true,
31614         /**
31615          * @event collapsed
31616          * Fires when this region is collapsed.
31617          * @param {Roo.LayoutRegion} this
31618          */
31619         "collapsed" : true,
31620         /**
31621          * @event expanded
31622          * Fires when this region is expanded.
31623          * @param {Roo.LayoutRegion} this
31624          */
31625         "expanded" : true,
31626         /**
31627          * @event slideshow
31628          * Fires when this region is slid into view.
31629          * @param {Roo.LayoutRegion} this
31630          */
31631         "slideshow" : true,
31632         /**
31633          * @event slidehide
31634          * Fires when this region slides out of view. 
31635          * @param {Roo.LayoutRegion} this
31636          */
31637         "slidehide" : true,
31638         /**
31639          * @event panelactivated
31640          * Fires when a panel is activated. 
31641          * @param {Roo.LayoutRegion} this
31642          * @param {Roo.ContentPanel} panel The activated panel
31643          */
31644         "panelactivated" : true,
31645         /**
31646          * @event resized
31647          * Fires when the user resizes this region. 
31648          * @param {Roo.LayoutRegion} this
31649          * @param {Number} newSize The new size (width for east/west, height for north/south)
31650          */
31651         "resized" : true
31652     };
31653     /** A collection of panels in this region. @type Roo.util.MixedCollection */
31654     this.panels = new Roo.util.MixedCollection();
31655     this.panels.getKey = this.getPanelId.createDelegate(this);
31656     this.box = null;
31657     this.activePanel = null;
31658     // ensure listeners are added...
31659     
31660     if (config.listeners || config.events) {
31661         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
31662             listeners : config.listeners || {},
31663             events : config.events || {}
31664         });
31665     }
31666     
31667     if(skipConfig !== true){
31668         this.applyConfig(config);
31669     }
31670 };
31671
31672 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
31673     getPanelId : function(p){
31674         return p.getId();
31675     },
31676     
31677     applyConfig : function(config){
31678         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
31679         this.config = config;
31680         
31681     },
31682     
31683     /**
31684      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
31685      * the width, for horizontal (north, south) the height.
31686      * @param {Number} newSize The new width or height
31687      */
31688     resizeTo : function(newSize){
31689         var el = this.el ? this.el :
31690                  (this.activePanel ? this.activePanel.getEl() : null);
31691         if(el){
31692             switch(this.position){
31693                 case "east":
31694                 case "west":
31695                     el.setWidth(newSize);
31696                     this.fireEvent("resized", this, newSize);
31697                 break;
31698                 case "north":
31699                 case "south":
31700                     el.setHeight(newSize);
31701                     this.fireEvent("resized", this, newSize);
31702                 break;                
31703             }
31704         }
31705     },
31706     
31707     getBox : function(){
31708         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
31709     },
31710     
31711     getMargins : function(){
31712         return this.margins;
31713     },
31714     
31715     updateBox : function(box){
31716         this.box = box;
31717         var el = this.activePanel.getEl();
31718         el.dom.style.left = box.x + "px";
31719         el.dom.style.top = box.y + "px";
31720         this.activePanel.setSize(box.width, box.height);
31721     },
31722     
31723     /**
31724      * Returns the container element for this region.
31725      * @return {Roo.Element}
31726      */
31727     getEl : function(){
31728         return this.activePanel;
31729     },
31730     
31731     /**
31732      * Returns true if this region is currently visible.
31733      * @return {Boolean}
31734      */
31735     isVisible : function(){
31736         return this.activePanel ? true : false;
31737     },
31738     
31739     setActivePanel : function(panel){
31740         panel = this.getPanel(panel);
31741         if(this.activePanel && this.activePanel != panel){
31742             this.activePanel.setActiveState(false);
31743             this.activePanel.getEl().setLeftTop(-10000,-10000);
31744         }
31745         this.activePanel = panel;
31746         panel.setActiveState(true);
31747         if(this.box){
31748             panel.setSize(this.box.width, this.box.height);
31749         }
31750         this.fireEvent("panelactivated", this, panel);
31751         this.fireEvent("invalidated");
31752     },
31753     
31754     /**
31755      * Show the specified panel.
31756      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
31757      * @return {Roo.ContentPanel} The shown panel or null
31758      */
31759     showPanel : function(panel){
31760         if(panel = this.getPanel(panel)){
31761             this.setActivePanel(panel);
31762         }
31763         return panel;
31764     },
31765     
31766     /**
31767      * Get the active panel for this region.
31768      * @return {Roo.ContentPanel} The active panel or null
31769      */
31770     getActivePanel : function(){
31771         return this.activePanel;
31772     },
31773     
31774     /**
31775      * Add the passed ContentPanel(s)
31776      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
31777      * @return {Roo.ContentPanel} The panel added (if only one was added)
31778      */
31779     add : function(panel){
31780         if(arguments.length > 1){
31781             for(var i = 0, len = arguments.length; i < len; i++) {
31782                 this.add(arguments[i]);
31783             }
31784             return null;
31785         }
31786         if(this.hasPanel(panel)){
31787             this.showPanel(panel);
31788             return panel;
31789         }
31790         var el = panel.getEl();
31791         if(el.dom.parentNode != this.mgr.el.dom){
31792             this.mgr.el.dom.appendChild(el.dom);
31793         }
31794         if(panel.setRegion){
31795             panel.setRegion(this);
31796         }
31797         this.panels.add(panel);
31798         el.setStyle("position", "absolute");
31799         if(!panel.background){
31800             this.setActivePanel(panel);
31801             if(this.config.initialSize && this.panels.getCount()==1){
31802                 this.resizeTo(this.config.initialSize);
31803             }
31804         }
31805         this.fireEvent("paneladded", this, panel);
31806         return panel;
31807     },
31808     
31809     /**
31810      * Returns true if the panel is in this region.
31811      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31812      * @return {Boolean}
31813      */
31814     hasPanel : function(panel){
31815         if(typeof panel == "object"){ // must be panel obj
31816             panel = panel.getId();
31817         }
31818         return this.getPanel(panel) ? true : false;
31819     },
31820     
31821     /**
31822      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
31823      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31824      * @param {Boolean} preservePanel Overrides the config preservePanel option
31825      * @return {Roo.ContentPanel} The panel that was removed
31826      */
31827     remove : function(panel, preservePanel){
31828         panel = this.getPanel(panel);
31829         if(!panel){
31830             return null;
31831         }
31832         var e = {};
31833         this.fireEvent("beforeremove", this, panel, e);
31834         if(e.cancel === true){
31835             return null;
31836         }
31837         var panelId = panel.getId();
31838         this.panels.removeKey(panelId);
31839         return panel;
31840     },
31841     
31842     /**
31843      * Returns the panel specified or null if it's not in this region.
31844      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31845      * @return {Roo.ContentPanel}
31846      */
31847     getPanel : function(id){
31848         if(typeof id == "object"){ // must be panel obj
31849             return id;
31850         }
31851         return this.panels.get(id);
31852     },
31853     
31854     /**
31855      * Returns this regions position (north/south/east/west/center).
31856      * @return {String} 
31857      */
31858     getPosition: function(){
31859         return this.position;    
31860     }
31861 });/*
31862  * Based on:
31863  * Ext JS Library 1.1.1
31864  * Copyright(c) 2006-2007, Ext JS, LLC.
31865  *
31866  * Originally Released Under LGPL - original licence link has changed is not relivant.
31867  *
31868  * Fork - LGPL
31869  * <script type="text/javascript">
31870  */
31871  
31872 /**
31873  * @class Roo.LayoutRegion
31874  * @extends Roo.BasicLayoutRegion
31875  * This class represents a region in a layout manager.
31876  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
31877  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
31878  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
31879  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
31880  * @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})
31881  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
31882  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
31883  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
31884  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
31885  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
31886  * @cfg {String}    title           The title for the region (overrides panel titles)
31887  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
31888  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
31889  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
31890  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
31891  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
31892  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
31893  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
31894  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
31895  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
31896  * @cfg {Boolean}   showPin         True to show a pin button
31897  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
31898  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
31899  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
31900  * @cfg {Number}    width           For East/West panels
31901  * @cfg {Number}    height          For North/South panels
31902  * @cfg {Boolean}   split           To show the splitter
31903  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
31904  */
31905 Roo.LayoutRegion = function(mgr, config, pos){
31906     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
31907     var dh = Roo.DomHelper;
31908     /** This region's container element 
31909     * @type Roo.Element */
31910     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
31911     /** This region's title element 
31912     * @type Roo.Element */
31913
31914     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
31915         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
31916         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
31917     ]}, true);
31918     this.titleEl.enableDisplayMode();
31919     /** This region's title text element 
31920     * @type HTMLElement */
31921     this.titleTextEl = this.titleEl.dom.firstChild;
31922     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
31923     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
31924     this.closeBtn.enableDisplayMode();
31925     this.closeBtn.on("click", this.closeClicked, this);
31926     this.closeBtn.hide();
31927
31928     this.createBody(config);
31929     this.visible = true;
31930     this.collapsed = false;
31931
31932     if(config.hideWhenEmpty){
31933         this.hide();
31934         this.on("paneladded", this.validateVisibility, this);
31935         this.on("panelremoved", this.validateVisibility, this);
31936     }
31937     this.applyConfig(config);
31938 };
31939
31940 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
31941
31942     createBody : function(){
31943         /** This region's body element 
31944         * @type Roo.Element */
31945         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
31946     },
31947
31948     applyConfig : function(c){
31949         if(c.collapsible && this.position != "center" && !this.collapsedEl){
31950             var dh = Roo.DomHelper;
31951             if(c.titlebar !== false){
31952                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
31953                 this.collapseBtn.on("click", this.collapse, this);
31954                 this.collapseBtn.enableDisplayMode();
31955
31956                 if(c.showPin === true || this.showPin){
31957                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
31958                     this.stickBtn.enableDisplayMode();
31959                     this.stickBtn.on("click", this.expand, this);
31960                     this.stickBtn.hide();
31961                 }
31962             }
31963             /** This region's collapsed element
31964             * @type Roo.Element */
31965             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
31966                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
31967             ]}, true);
31968             if(c.floatable !== false){
31969                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
31970                this.collapsedEl.on("click", this.collapseClick, this);
31971             }
31972
31973             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
31974                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
31975                    id: "message", unselectable: "on", style:{"float":"left"}});
31976                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
31977              }
31978             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
31979             this.expandBtn.on("click", this.expand, this);
31980         }
31981         if(this.collapseBtn){
31982             this.collapseBtn.setVisible(c.collapsible == true);
31983         }
31984         this.cmargins = c.cmargins || this.cmargins ||
31985                          (this.position == "west" || this.position == "east" ?
31986                              {top: 0, left: 2, right:2, bottom: 0} :
31987                              {top: 2, left: 0, right:0, bottom: 2});
31988         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
31989         this.bottomTabs = c.tabPosition != "top";
31990         this.autoScroll = c.autoScroll || false;
31991         if(this.autoScroll){
31992             this.bodyEl.setStyle("overflow", "auto");
31993         }else{
31994             this.bodyEl.setStyle("overflow", "hidden");
31995         }
31996         //if(c.titlebar !== false){
31997             if((!c.titlebar && !c.title) || c.titlebar === false){
31998                 this.titleEl.hide();
31999             }else{
32000                 this.titleEl.show();
32001                 if(c.title){
32002                     this.titleTextEl.innerHTML = c.title;
32003                 }
32004             }
32005         //}
32006         this.duration = c.duration || .30;
32007         this.slideDuration = c.slideDuration || .45;
32008         this.config = c;
32009         if(c.collapsed){
32010             this.collapse(true);
32011         }
32012         if(c.hidden){
32013             this.hide();
32014         }
32015     },
32016     /**
32017      * Returns true if this region is currently visible.
32018      * @return {Boolean}
32019      */
32020     isVisible : function(){
32021         return this.visible;
32022     },
32023
32024     /**
32025      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
32026      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
32027      */
32028     setCollapsedTitle : function(title){
32029         title = title || "&#160;";
32030         if(this.collapsedTitleTextEl){
32031             this.collapsedTitleTextEl.innerHTML = title;
32032         }
32033     },
32034
32035     getBox : function(){
32036         var b;
32037         if(!this.collapsed){
32038             b = this.el.getBox(false, true);
32039         }else{
32040             b = this.collapsedEl.getBox(false, true);
32041         }
32042         return b;
32043     },
32044
32045     getMargins : function(){
32046         return this.collapsed ? this.cmargins : this.margins;
32047     },
32048
32049     highlight : function(){
32050         this.el.addClass("x-layout-panel-dragover");
32051     },
32052
32053     unhighlight : function(){
32054         this.el.removeClass("x-layout-panel-dragover");
32055     },
32056
32057     updateBox : function(box){
32058         this.box = box;
32059         if(!this.collapsed){
32060             this.el.dom.style.left = box.x + "px";
32061             this.el.dom.style.top = box.y + "px";
32062             this.updateBody(box.width, box.height);
32063         }else{
32064             this.collapsedEl.dom.style.left = box.x + "px";
32065             this.collapsedEl.dom.style.top = box.y + "px";
32066             this.collapsedEl.setSize(box.width, box.height);
32067         }
32068         if(this.tabs){
32069             this.tabs.autoSizeTabs();
32070         }
32071     },
32072
32073     updateBody : function(w, h){
32074         if(w !== null){
32075             this.el.setWidth(w);
32076             w -= this.el.getBorderWidth("rl");
32077             if(this.config.adjustments){
32078                 w += this.config.adjustments[0];
32079             }
32080         }
32081         if(h !== null){
32082             this.el.setHeight(h);
32083             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
32084             h -= this.el.getBorderWidth("tb");
32085             if(this.config.adjustments){
32086                 h += this.config.adjustments[1];
32087             }
32088             this.bodyEl.setHeight(h);
32089             if(this.tabs){
32090                 h = this.tabs.syncHeight(h);
32091             }
32092         }
32093         if(this.panelSize){
32094             w = w !== null ? w : this.panelSize.width;
32095             h = h !== null ? h : this.panelSize.height;
32096         }
32097         if(this.activePanel){
32098             var el = this.activePanel.getEl();
32099             w = w !== null ? w : el.getWidth();
32100             h = h !== null ? h : el.getHeight();
32101             this.panelSize = {width: w, height: h};
32102             this.activePanel.setSize(w, h);
32103         }
32104         if(Roo.isIE && this.tabs){
32105             this.tabs.el.repaint();
32106         }
32107     },
32108
32109     /**
32110      * Returns the container element for this region.
32111      * @return {Roo.Element}
32112      */
32113     getEl : function(){
32114         return this.el;
32115     },
32116
32117     /**
32118      * Hides this region.
32119      */
32120     hide : function(){
32121         if(!this.collapsed){
32122             this.el.dom.style.left = "-2000px";
32123             this.el.hide();
32124         }else{
32125             this.collapsedEl.dom.style.left = "-2000px";
32126             this.collapsedEl.hide();
32127         }
32128         this.visible = false;
32129         this.fireEvent("visibilitychange", this, false);
32130     },
32131
32132     /**
32133      * Shows this region if it was previously hidden.
32134      */
32135     show : function(){
32136         if(!this.collapsed){
32137             this.el.show();
32138         }else{
32139             this.collapsedEl.show();
32140         }
32141         this.visible = true;
32142         this.fireEvent("visibilitychange", this, true);
32143     },
32144
32145     closeClicked : function(){
32146         if(this.activePanel){
32147             this.remove(this.activePanel);
32148         }
32149     },
32150
32151     collapseClick : function(e){
32152         if(this.isSlid){
32153            e.stopPropagation();
32154            this.slideIn();
32155         }else{
32156            e.stopPropagation();
32157            this.slideOut();
32158         }
32159     },
32160
32161     /**
32162      * Collapses this region.
32163      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
32164      */
32165     collapse : function(skipAnim){
32166         if(this.collapsed) return;
32167         this.collapsed = true;
32168         if(this.split){
32169             this.split.el.hide();
32170         }
32171         if(this.config.animate && skipAnim !== true){
32172             this.fireEvent("invalidated", this);
32173             this.animateCollapse();
32174         }else{
32175             this.el.setLocation(-20000,-20000);
32176             this.el.hide();
32177             this.collapsedEl.show();
32178             this.fireEvent("collapsed", this);
32179             this.fireEvent("invalidated", this);
32180         }
32181     },
32182
32183     animateCollapse : function(){
32184         // overridden
32185     },
32186
32187     /**
32188      * Expands this region if it was previously collapsed.
32189      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
32190      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
32191      */
32192     expand : function(e, skipAnim){
32193         if(e) e.stopPropagation();
32194         if(!this.collapsed || this.el.hasActiveFx()) return;
32195         if(this.isSlid){
32196             this.afterSlideIn();
32197             skipAnim = true;
32198         }
32199         this.collapsed = false;
32200         if(this.config.animate && skipAnim !== true){
32201             this.animateExpand();
32202         }else{
32203             this.el.show();
32204             if(this.split){
32205                 this.split.el.show();
32206             }
32207             this.collapsedEl.setLocation(-2000,-2000);
32208             this.collapsedEl.hide();
32209             this.fireEvent("invalidated", this);
32210             this.fireEvent("expanded", this);
32211         }
32212     },
32213
32214     animateExpand : function(){
32215         // overridden
32216     },
32217
32218     initTabs : function()
32219     {
32220         this.bodyEl.setStyle("overflow", "hidden");
32221         var ts = new Roo.TabPanel(
32222                 this.bodyEl.dom,
32223                 {
32224                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
32225                     disableTooltips: this.config.disableTabTips,
32226                     toolbar : this.config.toolbar
32227                 }
32228         );
32229         if(this.config.hideTabs){
32230             ts.stripWrap.setDisplayed(false);
32231         }
32232         this.tabs = ts;
32233         ts.resizeTabs = this.config.resizeTabs === true;
32234         ts.minTabWidth = this.config.minTabWidth || 40;
32235         ts.maxTabWidth = this.config.maxTabWidth || 250;
32236         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
32237         ts.monitorResize = false;
32238         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32239         ts.bodyEl.addClass('x-layout-tabs-body');
32240         this.panels.each(this.initPanelAsTab, this);
32241     },
32242
32243     initPanelAsTab : function(panel){
32244         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
32245                     this.config.closeOnTab && panel.isClosable());
32246         if(panel.tabTip !== undefined){
32247             ti.setTooltip(panel.tabTip);
32248         }
32249         ti.on("activate", function(){
32250               this.setActivePanel(panel);
32251         }, this);
32252         if(this.config.closeOnTab){
32253             ti.on("beforeclose", function(t, e){
32254                 e.cancel = true;
32255                 this.remove(panel);
32256             }, this);
32257         }
32258         return ti;
32259     },
32260
32261     updatePanelTitle : function(panel, title){
32262         if(this.activePanel == panel){
32263             this.updateTitle(title);
32264         }
32265         if(this.tabs){
32266             var ti = this.tabs.getTab(panel.getEl().id);
32267             ti.setText(title);
32268             if(panel.tabTip !== undefined){
32269                 ti.setTooltip(panel.tabTip);
32270             }
32271         }
32272     },
32273
32274     updateTitle : function(title){
32275         if(this.titleTextEl && !this.config.title){
32276             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
32277         }
32278     },
32279
32280     setActivePanel : function(panel){
32281         panel = this.getPanel(panel);
32282         if(this.activePanel && this.activePanel != panel){
32283             this.activePanel.setActiveState(false);
32284         }
32285         this.activePanel = panel;
32286         panel.setActiveState(true);
32287         if(this.panelSize){
32288             panel.setSize(this.panelSize.width, this.panelSize.height);
32289         }
32290         if(this.closeBtn){
32291             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
32292         }
32293         this.updateTitle(panel.getTitle());
32294         if(this.tabs){
32295             this.fireEvent("invalidated", this);
32296         }
32297         this.fireEvent("panelactivated", this, panel);
32298     },
32299
32300     /**
32301      * Shows the specified panel.
32302      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
32303      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
32304      */
32305     showPanel : function(panel){
32306         if(panel = this.getPanel(panel)){
32307             if(this.tabs){
32308                 var tab = this.tabs.getTab(panel.getEl().id);
32309                 if(tab.isHidden()){
32310                     this.tabs.unhideTab(tab.id);
32311                 }
32312                 tab.activate();
32313             }else{
32314                 this.setActivePanel(panel);
32315             }
32316         }
32317         return panel;
32318     },
32319
32320     /**
32321      * Get the active panel for this region.
32322      * @return {Roo.ContentPanel} The active panel or null
32323      */
32324     getActivePanel : function(){
32325         return this.activePanel;
32326     },
32327
32328     validateVisibility : function(){
32329         if(this.panels.getCount() < 1){
32330             this.updateTitle("&#160;");
32331             this.closeBtn.hide();
32332             this.hide();
32333         }else{
32334             if(!this.isVisible()){
32335                 this.show();
32336             }
32337         }
32338     },
32339
32340     /**
32341      * Adds the passed ContentPanel(s) to this region.
32342      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32343      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
32344      */
32345     add : function(panel){
32346         if(arguments.length > 1){
32347             for(var i = 0, len = arguments.length; i < len; i++) {
32348                 this.add(arguments[i]);
32349             }
32350             return null;
32351         }
32352         if(this.hasPanel(panel)){
32353             this.showPanel(panel);
32354             return panel;
32355         }
32356         panel.setRegion(this);
32357         this.panels.add(panel);
32358         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
32359             this.bodyEl.dom.appendChild(panel.getEl().dom);
32360             if(panel.background !== true){
32361                 this.setActivePanel(panel);
32362             }
32363             this.fireEvent("paneladded", this, panel);
32364             return panel;
32365         }
32366         if(!this.tabs){
32367             this.initTabs();
32368         }else{
32369             this.initPanelAsTab(panel);
32370         }
32371         if(panel.background !== true){
32372             this.tabs.activate(panel.getEl().id);
32373         }
32374         this.fireEvent("paneladded", this, panel);
32375         return panel;
32376     },
32377
32378     /**
32379      * Hides the tab for the specified panel.
32380      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32381      */
32382     hidePanel : function(panel){
32383         if(this.tabs && (panel = this.getPanel(panel))){
32384             this.tabs.hideTab(panel.getEl().id);
32385         }
32386     },
32387
32388     /**
32389      * Unhides the tab for a previously hidden panel.
32390      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32391      */
32392     unhidePanel : function(panel){
32393         if(this.tabs && (panel = this.getPanel(panel))){
32394             this.tabs.unhideTab(panel.getEl().id);
32395         }
32396     },
32397
32398     clearPanels : function(){
32399         while(this.panels.getCount() > 0){
32400              this.remove(this.panels.first());
32401         }
32402     },
32403
32404     /**
32405      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32406      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32407      * @param {Boolean} preservePanel Overrides the config preservePanel option
32408      * @return {Roo.ContentPanel} The panel that was removed
32409      */
32410     remove : function(panel, preservePanel){
32411         panel = this.getPanel(panel);
32412         if(!panel){
32413             return null;
32414         }
32415         var e = {};
32416         this.fireEvent("beforeremove", this, panel, e);
32417         if(e.cancel === true){
32418             return null;
32419         }
32420         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
32421         var panelId = panel.getId();
32422         this.panels.removeKey(panelId);
32423         if(preservePanel){
32424             document.body.appendChild(panel.getEl().dom);
32425         }
32426         if(this.tabs){
32427             this.tabs.removeTab(panel.getEl().id);
32428         }else if (!preservePanel){
32429             this.bodyEl.dom.removeChild(panel.getEl().dom);
32430         }
32431         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
32432             var p = this.panels.first();
32433             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
32434             tempEl.appendChild(p.getEl().dom);
32435             this.bodyEl.update("");
32436             this.bodyEl.dom.appendChild(p.getEl().dom);
32437             tempEl = null;
32438             this.updateTitle(p.getTitle());
32439             this.tabs = null;
32440             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32441             this.setActivePanel(p);
32442         }
32443         panel.setRegion(null);
32444         if(this.activePanel == panel){
32445             this.activePanel = null;
32446         }
32447         if(this.config.autoDestroy !== false && preservePanel !== true){
32448             try{panel.destroy();}catch(e){}
32449         }
32450         this.fireEvent("panelremoved", this, panel);
32451         return panel;
32452     },
32453
32454     /**
32455      * Returns the TabPanel component used by this region
32456      * @return {Roo.TabPanel}
32457      */
32458     getTabs : function(){
32459         return this.tabs;
32460     },
32461
32462     createTool : function(parentEl, className){
32463         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
32464             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
32465         btn.addClassOnOver("x-layout-tools-button-over");
32466         return btn;
32467     }
32468 });/*
32469  * Based on:
32470  * Ext JS Library 1.1.1
32471  * Copyright(c) 2006-2007, Ext JS, LLC.
32472  *
32473  * Originally Released Under LGPL - original licence link has changed is not relivant.
32474  *
32475  * Fork - LGPL
32476  * <script type="text/javascript">
32477  */
32478  
32479
32480
32481 /**
32482  * @class Roo.SplitLayoutRegion
32483  * @extends Roo.LayoutRegion
32484  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
32485  */
32486 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
32487     this.cursor = cursor;
32488     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
32489 };
32490
32491 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
32492     splitTip : "Drag to resize.",
32493     collapsibleSplitTip : "Drag to resize. Double click to hide.",
32494     useSplitTips : false,
32495
32496     applyConfig : function(config){
32497         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
32498         if(config.split){
32499             if(!this.split){
32500                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
32501                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
32502                 /** The SplitBar for this region 
32503                 * @type Roo.SplitBar */
32504                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
32505                 this.split.on("moved", this.onSplitMove, this);
32506                 this.split.useShim = config.useShim === true;
32507                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
32508                 if(this.useSplitTips){
32509                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
32510                 }
32511                 if(config.collapsible){
32512                     this.split.el.on("dblclick", this.collapse,  this);
32513                 }
32514             }
32515             if(typeof config.minSize != "undefined"){
32516                 this.split.minSize = config.minSize;
32517             }
32518             if(typeof config.maxSize != "undefined"){
32519                 this.split.maxSize = config.maxSize;
32520             }
32521             if(config.hideWhenEmpty || config.hidden || config.collapsed){
32522                 this.hideSplitter();
32523             }
32524         }
32525     },
32526
32527     getHMaxSize : function(){
32528          var cmax = this.config.maxSize || 10000;
32529          var center = this.mgr.getRegion("center");
32530          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
32531     },
32532
32533     getVMaxSize : function(){
32534          var cmax = this.config.maxSize || 10000;
32535          var center = this.mgr.getRegion("center");
32536          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
32537     },
32538
32539     onSplitMove : function(split, newSize){
32540         this.fireEvent("resized", this, newSize);
32541     },
32542     
32543     /** 
32544      * Returns the {@link Roo.SplitBar} for this region.
32545      * @return {Roo.SplitBar}
32546      */
32547     getSplitBar : function(){
32548         return this.split;
32549     },
32550     
32551     hide : function(){
32552         this.hideSplitter();
32553         Roo.SplitLayoutRegion.superclass.hide.call(this);
32554     },
32555
32556     hideSplitter : function(){
32557         if(this.split){
32558             this.split.el.setLocation(-2000,-2000);
32559             this.split.el.hide();
32560         }
32561     },
32562
32563     show : function(){
32564         if(this.split){
32565             this.split.el.show();
32566         }
32567         Roo.SplitLayoutRegion.superclass.show.call(this);
32568     },
32569     
32570     beforeSlide: function(){
32571         if(Roo.isGecko){// firefox overflow auto bug workaround
32572             this.bodyEl.clip();
32573             if(this.tabs) this.tabs.bodyEl.clip();
32574             if(this.activePanel){
32575                 this.activePanel.getEl().clip();
32576                 
32577                 if(this.activePanel.beforeSlide){
32578                     this.activePanel.beforeSlide();
32579                 }
32580             }
32581         }
32582     },
32583     
32584     afterSlide : function(){
32585         if(Roo.isGecko){// firefox overflow auto bug workaround
32586             this.bodyEl.unclip();
32587             if(this.tabs) this.tabs.bodyEl.unclip();
32588             if(this.activePanel){
32589                 this.activePanel.getEl().unclip();
32590                 if(this.activePanel.afterSlide){
32591                     this.activePanel.afterSlide();
32592                 }
32593             }
32594         }
32595     },
32596
32597     initAutoHide : function(){
32598         if(this.autoHide !== false){
32599             if(!this.autoHideHd){
32600                 var st = new Roo.util.DelayedTask(this.slideIn, this);
32601                 this.autoHideHd = {
32602                     "mouseout": function(e){
32603                         if(!e.within(this.el, true)){
32604                             st.delay(500);
32605                         }
32606                     },
32607                     "mouseover" : function(e){
32608                         st.cancel();
32609                     },
32610                     scope : this
32611                 };
32612             }
32613             this.el.on(this.autoHideHd);
32614         }
32615     },
32616
32617     clearAutoHide : function(){
32618         if(this.autoHide !== false){
32619             this.el.un("mouseout", this.autoHideHd.mouseout);
32620             this.el.un("mouseover", this.autoHideHd.mouseover);
32621         }
32622     },
32623
32624     clearMonitor : function(){
32625         Roo.get(document).un("click", this.slideInIf, this);
32626     },
32627
32628     // these names are backwards but not changed for compat
32629     slideOut : function(){
32630         if(this.isSlid || this.el.hasActiveFx()){
32631             return;
32632         }
32633         this.isSlid = true;
32634         if(this.collapseBtn){
32635             this.collapseBtn.hide();
32636         }
32637         this.closeBtnState = this.closeBtn.getStyle('display');
32638         this.closeBtn.hide();
32639         if(this.stickBtn){
32640             this.stickBtn.show();
32641         }
32642         this.el.show();
32643         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
32644         this.beforeSlide();
32645         this.el.setStyle("z-index", 10001);
32646         this.el.slideIn(this.getSlideAnchor(), {
32647             callback: function(){
32648                 this.afterSlide();
32649                 this.initAutoHide();
32650                 Roo.get(document).on("click", this.slideInIf, this);
32651                 this.fireEvent("slideshow", this);
32652             },
32653             scope: this,
32654             block: true
32655         });
32656     },
32657
32658     afterSlideIn : function(){
32659         this.clearAutoHide();
32660         this.isSlid = false;
32661         this.clearMonitor();
32662         this.el.setStyle("z-index", "");
32663         if(this.collapseBtn){
32664             this.collapseBtn.show();
32665         }
32666         this.closeBtn.setStyle('display', this.closeBtnState);
32667         if(this.stickBtn){
32668             this.stickBtn.hide();
32669         }
32670         this.fireEvent("slidehide", this);
32671     },
32672
32673     slideIn : function(cb){
32674         if(!this.isSlid || this.el.hasActiveFx()){
32675             Roo.callback(cb);
32676             return;
32677         }
32678         this.isSlid = false;
32679         this.beforeSlide();
32680         this.el.slideOut(this.getSlideAnchor(), {
32681             callback: function(){
32682                 this.el.setLeftTop(-10000, -10000);
32683                 this.afterSlide();
32684                 this.afterSlideIn();
32685                 Roo.callback(cb);
32686             },
32687             scope: this,
32688             block: true
32689         });
32690     },
32691     
32692     slideInIf : function(e){
32693         if(!e.within(this.el)){
32694             this.slideIn();
32695         }
32696     },
32697
32698     animateCollapse : function(){
32699         this.beforeSlide();
32700         this.el.setStyle("z-index", 20000);
32701         var anchor = this.getSlideAnchor();
32702         this.el.slideOut(anchor, {
32703             callback : function(){
32704                 this.el.setStyle("z-index", "");
32705                 this.collapsedEl.slideIn(anchor, {duration:.3});
32706                 this.afterSlide();
32707                 this.el.setLocation(-10000,-10000);
32708                 this.el.hide();
32709                 this.fireEvent("collapsed", this);
32710             },
32711             scope: this,
32712             block: true
32713         });
32714     },
32715
32716     animateExpand : function(){
32717         this.beforeSlide();
32718         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
32719         this.el.setStyle("z-index", 20000);
32720         this.collapsedEl.hide({
32721             duration:.1
32722         });
32723         this.el.slideIn(this.getSlideAnchor(), {
32724             callback : function(){
32725                 this.el.setStyle("z-index", "");
32726                 this.afterSlide();
32727                 if(this.split){
32728                     this.split.el.show();
32729                 }
32730                 this.fireEvent("invalidated", this);
32731                 this.fireEvent("expanded", this);
32732             },
32733             scope: this,
32734             block: true
32735         });
32736     },
32737
32738     anchors : {
32739         "west" : "left",
32740         "east" : "right",
32741         "north" : "top",
32742         "south" : "bottom"
32743     },
32744
32745     sanchors : {
32746         "west" : "l",
32747         "east" : "r",
32748         "north" : "t",
32749         "south" : "b"
32750     },
32751
32752     canchors : {
32753         "west" : "tl-tr",
32754         "east" : "tr-tl",
32755         "north" : "tl-bl",
32756         "south" : "bl-tl"
32757     },
32758
32759     getAnchor : function(){
32760         return this.anchors[this.position];
32761     },
32762
32763     getCollapseAnchor : function(){
32764         return this.canchors[this.position];
32765     },
32766
32767     getSlideAnchor : function(){
32768         return this.sanchors[this.position];
32769     },
32770
32771     getAlignAdj : function(){
32772         var cm = this.cmargins;
32773         switch(this.position){
32774             case "west":
32775                 return [0, 0];
32776             break;
32777             case "east":
32778                 return [0, 0];
32779             break;
32780             case "north":
32781                 return [0, 0];
32782             break;
32783             case "south":
32784                 return [0, 0];
32785             break;
32786         }
32787     },
32788
32789     getExpandAdj : function(){
32790         var c = this.collapsedEl, cm = this.cmargins;
32791         switch(this.position){
32792             case "west":
32793                 return [-(cm.right+c.getWidth()+cm.left), 0];
32794             break;
32795             case "east":
32796                 return [cm.right+c.getWidth()+cm.left, 0];
32797             break;
32798             case "north":
32799                 return [0, -(cm.top+cm.bottom+c.getHeight())];
32800             break;
32801             case "south":
32802                 return [0, cm.top+cm.bottom+c.getHeight()];
32803             break;
32804         }
32805     }
32806 });/*
32807  * Based on:
32808  * Ext JS Library 1.1.1
32809  * Copyright(c) 2006-2007, Ext JS, LLC.
32810  *
32811  * Originally Released Under LGPL - original licence link has changed is not relivant.
32812  *
32813  * Fork - LGPL
32814  * <script type="text/javascript">
32815  */
32816 /*
32817  * These classes are private internal classes
32818  */
32819 Roo.CenterLayoutRegion = function(mgr, config){
32820     Roo.LayoutRegion.call(this, mgr, config, "center");
32821     this.visible = true;
32822     this.minWidth = config.minWidth || 20;
32823     this.minHeight = config.minHeight || 20;
32824 };
32825
32826 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
32827     hide : function(){
32828         // center panel can't be hidden
32829     },
32830     
32831     show : function(){
32832         // center panel can't be hidden
32833     },
32834     
32835     getMinWidth: function(){
32836         return this.minWidth;
32837     },
32838     
32839     getMinHeight: function(){
32840         return this.minHeight;
32841     }
32842 });
32843
32844
32845 Roo.NorthLayoutRegion = function(mgr, config){
32846     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
32847     if(this.split){
32848         this.split.placement = Roo.SplitBar.TOP;
32849         this.split.orientation = Roo.SplitBar.VERTICAL;
32850         this.split.el.addClass("x-layout-split-v");
32851     }
32852     var size = config.initialSize || config.height;
32853     if(typeof size != "undefined"){
32854         this.el.setHeight(size);
32855     }
32856 };
32857 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
32858     orientation: Roo.SplitBar.VERTICAL,
32859     getBox : function(){
32860         if(this.collapsed){
32861             return this.collapsedEl.getBox();
32862         }
32863         var box = this.el.getBox();
32864         if(this.split){
32865             box.height += this.split.el.getHeight();
32866         }
32867         return box;
32868     },
32869     
32870     updateBox : function(box){
32871         if(this.split && !this.collapsed){
32872             box.height -= this.split.el.getHeight();
32873             this.split.el.setLeft(box.x);
32874             this.split.el.setTop(box.y+box.height);
32875             this.split.el.setWidth(box.width);
32876         }
32877         if(this.collapsed){
32878             this.updateBody(box.width, null);
32879         }
32880         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32881     }
32882 });
32883
32884 Roo.SouthLayoutRegion = function(mgr, config){
32885     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
32886     if(this.split){
32887         this.split.placement = Roo.SplitBar.BOTTOM;
32888         this.split.orientation = Roo.SplitBar.VERTICAL;
32889         this.split.el.addClass("x-layout-split-v");
32890     }
32891     var size = config.initialSize || config.height;
32892     if(typeof size != "undefined"){
32893         this.el.setHeight(size);
32894     }
32895 };
32896 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
32897     orientation: Roo.SplitBar.VERTICAL,
32898     getBox : function(){
32899         if(this.collapsed){
32900             return this.collapsedEl.getBox();
32901         }
32902         var box = this.el.getBox();
32903         if(this.split){
32904             var sh = this.split.el.getHeight();
32905             box.height += sh;
32906             box.y -= sh;
32907         }
32908         return box;
32909     },
32910     
32911     updateBox : function(box){
32912         if(this.split && !this.collapsed){
32913             var sh = this.split.el.getHeight();
32914             box.height -= sh;
32915             box.y += sh;
32916             this.split.el.setLeft(box.x);
32917             this.split.el.setTop(box.y-sh);
32918             this.split.el.setWidth(box.width);
32919         }
32920         if(this.collapsed){
32921             this.updateBody(box.width, null);
32922         }
32923         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32924     }
32925 });
32926
32927 Roo.EastLayoutRegion = function(mgr, config){
32928     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
32929     if(this.split){
32930         this.split.placement = Roo.SplitBar.RIGHT;
32931         this.split.orientation = Roo.SplitBar.HORIZONTAL;
32932         this.split.el.addClass("x-layout-split-h");
32933     }
32934     var size = config.initialSize || config.width;
32935     if(typeof size != "undefined"){
32936         this.el.setWidth(size);
32937     }
32938 };
32939 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
32940     orientation: Roo.SplitBar.HORIZONTAL,
32941     getBox : function(){
32942         if(this.collapsed){
32943             return this.collapsedEl.getBox();
32944         }
32945         var box = this.el.getBox();
32946         if(this.split){
32947             var sw = this.split.el.getWidth();
32948             box.width += sw;
32949             box.x -= sw;
32950         }
32951         return box;
32952     },
32953
32954     updateBox : function(box){
32955         if(this.split && !this.collapsed){
32956             var sw = this.split.el.getWidth();
32957             box.width -= sw;
32958             this.split.el.setLeft(box.x);
32959             this.split.el.setTop(box.y);
32960             this.split.el.setHeight(box.height);
32961             box.x += sw;
32962         }
32963         if(this.collapsed){
32964             this.updateBody(null, box.height);
32965         }
32966         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32967     }
32968 });
32969
32970 Roo.WestLayoutRegion = function(mgr, config){
32971     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
32972     if(this.split){
32973         this.split.placement = Roo.SplitBar.LEFT;
32974         this.split.orientation = Roo.SplitBar.HORIZONTAL;
32975         this.split.el.addClass("x-layout-split-h");
32976     }
32977     var size = config.initialSize || config.width;
32978     if(typeof size != "undefined"){
32979         this.el.setWidth(size);
32980     }
32981 };
32982 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
32983     orientation: Roo.SplitBar.HORIZONTAL,
32984     getBox : function(){
32985         if(this.collapsed){
32986             return this.collapsedEl.getBox();
32987         }
32988         var box = this.el.getBox();
32989         if(this.split){
32990             box.width += this.split.el.getWidth();
32991         }
32992         return box;
32993     },
32994     
32995     updateBox : function(box){
32996         if(this.split && !this.collapsed){
32997             var sw = this.split.el.getWidth();
32998             box.width -= sw;
32999             this.split.el.setLeft(box.x+box.width);
33000             this.split.el.setTop(box.y);
33001             this.split.el.setHeight(box.height);
33002         }
33003         if(this.collapsed){
33004             this.updateBody(null, box.height);
33005         }
33006         Roo.LayoutRegion.prototype.updateBox.call(this, box);
33007     }
33008 });
33009 /*
33010  * Based on:
33011  * Ext JS Library 1.1.1
33012  * Copyright(c) 2006-2007, Ext JS, LLC.
33013  *
33014  * Originally Released Under LGPL - original licence link has changed is not relivant.
33015  *
33016  * Fork - LGPL
33017  * <script type="text/javascript">
33018  */
33019  
33020  
33021 /*
33022  * Private internal class for reading and applying state
33023  */
33024 Roo.LayoutStateManager = function(layout){
33025      // default empty state
33026      this.state = {
33027         north: {},
33028         south: {},
33029         east: {},
33030         west: {}       
33031     };
33032 };
33033
33034 Roo.LayoutStateManager.prototype = {
33035     init : function(layout, provider){
33036         this.provider = provider;
33037         var state = provider.get(layout.id+"-layout-state");
33038         if(state){
33039             var wasUpdating = layout.isUpdating();
33040             if(!wasUpdating){
33041                 layout.beginUpdate();
33042             }
33043             for(var key in state){
33044                 if(typeof state[key] != "function"){
33045                     var rstate = state[key];
33046                     var r = layout.getRegion(key);
33047                     if(r && rstate){
33048                         if(rstate.size){
33049                             r.resizeTo(rstate.size);
33050                         }
33051                         if(rstate.collapsed == true){
33052                             r.collapse(true);
33053                         }else{
33054                             r.expand(null, true);
33055                         }
33056                     }
33057                 }
33058             }
33059             if(!wasUpdating){
33060                 layout.endUpdate();
33061             }
33062             this.state = state; 
33063         }
33064         this.layout = layout;
33065         layout.on("regionresized", this.onRegionResized, this);
33066         layout.on("regioncollapsed", this.onRegionCollapsed, this);
33067         layout.on("regionexpanded", this.onRegionExpanded, this);
33068     },
33069     
33070     storeState : function(){
33071         this.provider.set(this.layout.id+"-layout-state", this.state);
33072     },
33073     
33074     onRegionResized : function(region, newSize){
33075         this.state[region.getPosition()].size = newSize;
33076         this.storeState();
33077     },
33078     
33079     onRegionCollapsed : function(region){
33080         this.state[region.getPosition()].collapsed = true;
33081         this.storeState();
33082     },
33083     
33084     onRegionExpanded : function(region){
33085         this.state[region.getPosition()].collapsed = false;
33086         this.storeState();
33087     }
33088 };/*
33089  * Based on:
33090  * Ext JS Library 1.1.1
33091  * Copyright(c) 2006-2007, Ext JS, LLC.
33092  *
33093  * Originally Released Under LGPL - original licence link has changed is not relivant.
33094  *
33095  * Fork - LGPL
33096  * <script type="text/javascript">
33097  */
33098 /**
33099  * @class Roo.ContentPanel
33100  * @extends Roo.util.Observable
33101  * A basic ContentPanel element.
33102  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
33103  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
33104  * @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
33105  * @cfg {Boolean}   closable      True if the panel can be closed/removed
33106  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
33107  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
33108  * @cfg {Toolbar}   toolbar       A toolbar for this panel
33109  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
33110  * @cfg {String} title          The title for this panel
33111  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
33112  * @cfg {String} url            Calls {@link #setUrl} with this value
33113  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
33114  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
33115  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
33116  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
33117
33118  * @constructor
33119  * Create a new ContentPanel.
33120  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
33121  * @param {String/Object} config A string to set only the title or a config object
33122  * @param {String} content (optional) Set the HTML content for this panel
33123  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
33124  */
33125 Roo.ContentPanel = function(el, config, content){
33126     
33127      
33128     /*
33129     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
33130         config = el;
33131         el = Roo.id();
33132     }
33133     if (config && config.parentLayout) { 
33134         el = config.parentLayout.el.createChild(); 
33135     }
33136     */
33137     if(el.autoCreate){ // xtype is available if this is called from factory
33138         config = el;
33139         el = Roo.id();
33140     }
33141     this.el = Roo.get(el);
33142     if(!this.el && config && config.autoCreate){
33143         if(typeof config.autoCreate == "object"){
33144             if(!config.autoCreate.id){
33145                 config.autoCreate.id = config.id||el;
33146             }
33147             this.el = Roo.DomHelper.append(document.body,
33148                         config.autoCreate, true);
33149         }else{
33150             this.el = Roo.DomHelper.append(document.body,
33151                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
33152         }
33153     }
33154     this.closable = false;
33155     this.loaded = false;
33156     this.active = false;
33157     if(typeof config == "string"){
33158         this.title = config;
33159     }else{
33160         Roo.apply(this, config);
33161     }
33162     
33163     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
33164         this.wrapEl = this.el.wrap();
33165         this.toolbar.container = this.el.insertSibling(false, 'before');
33166         this.toolbar = new Roo.Toolbar(this.toolbar);
33167     }
33168     
33169     // xtype created footer. - not sure if will work as we normally have to render first..
33170     if (this.footer && !this.footer.el && this.footer.xtype) {
33171         if (!this.wrapEl) {
33172             this.wrapEl = this.el.wrap();
33173         }
33174     
33175         this.footer.container = this.wrapEl.createChild();
33176          
33177         this.footer = Roo.factory(this.footer, Roo);
33178         
33179     }
33180     
33181     if(this.resizeEl){
33182         this.resizeEl = Roo.get(this.resizeEl, true);
33183     }else{
33184         this.resizeEl = this.el;
33185     }
33186     this.addEvents({
33187         /**
33188          * @event activate
33189          * Fires when this panel is activated. 
33190          * @param {Roo.ContentPanel} this
33191          */
33192         "activate" : true,
33193         /**
33194          * @event deactivate
33195          * Fires when this panel is activated. 
33196          * @param {Roo.ContentPanel} this
33197          */
33198         "deactivate" : true,
33199
33200         /**
33201          * @event resize
33202          * Fires when this panel is resized if fitToFrame is true.
33203          * @param {Roo.ContentPanel} this
33204          * @param {Number} width The width after any component adjustments
33205          * @param {Number} height The height after any component adjustments
33206          */
33207         "resize" : true,
33208         
33209          /**
33210          * @event render
33211          * Fires when this tab is created
33212          * @param {Roo.ContentPanel} this
33213          */
33214         "render" : true
33215         
33216         
33217         
33218     });
33219     if(this.autoScroll){
33220         this.resizeEl.setStyle("overflow", "auto");
33221     } else {
33222         // fix randome scrolling
33223         this.el.on('scroll', function() {
33224             Roo.log('fix random scolling');
33225             this.scrollTo('top',0); 
33226         });
33227     }
33228     content = content || this.content;
33229     if(content){
33230         this.setContent(content);
33231     }
33232     if(config && config.url){
33233         this.setUrl(this.url, this.params, this.loadOnce);
33234     }
33235     
33236     
33237     
33238     Roo.ContentPanel.superclass.constructor.call(this);
33239     
33240     this.fireEvent('render', this);
33241 };
33242
33243 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
33244     tabTip:'',
33245     setRegion : function(region){
33246         this.region = region;
33247         if(region){
33248            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
33249         }else{
33250            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
33251         } 
33252     },
33253     
33254     /**
33255      * Returns the toolbar for this Panel if one was configured. 
33256      * @return {Roo.Toolbar} 
33257      */
33258     getToolbar : function(){
33259         return this.toolbar;
33260     },
33261     
33262     setActiveState : function(active){
33263         this.active = active;
33264         if(!active){
33265             this.fireEvent("deactivate", this);
33266         }else{
33267             this.fireEvent("activate", this);
33268         }
33269     },
33270     /**
33271      * Updates this panel's element
33272      * @param {String} content The new content
33273      * @param {Boolean} loadScripts (optional) true to look for and process scripts
33274     */
33275     setContent : function(content, loadScripts){
33276         this.el.update(content, loadScripts);
33277     },
33278
33279     ignoreResize : function(w, h){
33280         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
33281             return true;
33282         }else{
33283             this.lastSize = {width: w, height: h};
33284             return false;
33285         }
33286     },
33287     /**
33288      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
33289      * @return {Roo.UpdateManager} The UpdateManager
33290      */
33291     getUpdateManager : function(){
33292         return this.el.getUpdateManager();
33293     },
33294      /**
33295      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
33296      * @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:
33297 <pre><code>
33298 panel.load({
33299     url: "your-url.php",
33300     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
33301     callback: yourFunction,
33302     scope: yourObject, //(optional scope)
33303     discardUrl: false,
33304     nocache: false,
33305     text: "Loading...",
33306     timeout: 30,
33307     scripts: false
33308 });
33309 </code></pre>
33310      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
33311      * 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.
33312      * @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}
33313      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
33314      * @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.
33315      * @return {Roo.ContentPanel} this
33316      */
33317     load : function(){
33318         var um = this.el.getUpdateManager();
33319         um.update.apply(um, arguments);
33320         return this;
33321     },
33322
33323
33324     /**
33325      * 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.
33326      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
33327      * @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)
33328      * @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)
33329      * @return {Roo.UpdateManager} The UpdateManager
33330      */
33331     setUrl : function(url, params, loadOnce){
33332         if(this.refreshDelegate){
33333             this.removeListener("activate", this.refreshDelegate);
33334         }
33335         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
33336         this.on("activate", this.refreshDelegate);
33337         return this.el.getUpdateManager();
33338     },
33339     
33340     _handleRefresh : function(url, params, loadOnce){
33341         if(!loadOnce || !this.loaded){
33342             var updater = this.el.getUpdateManager();
33343             updater.update(url, params, this._setLoaded.createDelegate(this));
33344         }
33345     },
33346     
33347     _setLoaded : function(){
33348         this.loaded = true;
33349     }, 
33350     
33351     /**
33352      * Returns this panel's id
33353      * @return {String} 
33354      */
33355     getId : function(){
33356         return this.el.id;
33357     },
33358     
33359     /** 
33360      * Returns this panel's element - used by regiosn to add.
33361      * @return {Roo.Element} 
33362      */
33363     getEl : function(){
33364         return this.wrapEl || this.el;
33365     },
33366     
33367     adjustForComponents : function(width, height)
33368     {
33369         Roo.log('adjustForComponents ');
33370         if(this.resizeEl != this.el){
33371             width -= this.el.getFrameWidth('lr');
33372             height -= this.el.getFrameWidth('tb');
33373         }
33374         if(this.toolbar){
33375             var te = this.toolbar.getEl();
33376             height -= te.getHeight();
33377             te.setWidth(width);
33378         }
33379         if(this.footer){
33380             var te = this.footer.getEl();
33381             Roo.log("footer:" + te.getHeight());
33382             
33383             height -= te.getHeight();
33384             te.setWidth(width);
33385         }
33386         
33387         
33388         if(this.adjustments){
33389             width += this.adjustments[0];
33390             height += this.adjustments[1];
33391         }
33392         return {"width": width, "height": height};
33393     },
33394     
33395     setSize : function(width, height){
33396         if(this.fitToFrame && !this.ignoreResize(width, height)){
33397             if(this.fitContainer && this.resizeEl != this.el){
33398                 this.el.setSize(width, height);
33399             }
33400             var size = this.adjustForComponents(width, height);
33401             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
33402             this.fireEvent('resize', this, size.width, size.height);
33403         }
33404     },
33405     
33406     /**
33407      * Returns this panel's title
33408      * @return {String} 
33409      */
33410     getTitle : function(){
33411         return this.title;
33412     },
33413     
33414     /**
33415      * Set this panel's title
33416      * @param {String} title
33417      */
33418     setTitle : function(title){
33419         this.title = title;
33420         if(this.region){
33421             this.region.updatePanelTitle(this, title);
33422         }
33423     },
33424     
33425     /**
33426      * Returns true is this panel was configured to be closable
33427      * @return {Boolean} 
33428      */
33429     isClosable : function(){
33430         return this.closable;
33431     },
33432     
33433     beforeSlide : function(){
33434         this.el.clip();
33435         this.resizeEl.clip();
33436     },
33437     
33438     afterSlide : function(){
33439         this.el.unclip();
33440         this.resizeEl.unclip();
33441     },
33442     
33443     /**
33444      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
33445      *   Will fail silently if the {@link #setUrl} method has not been called.
33446      *   This does not activate the panel, just updates its content.
33447      */
33448     refresh : function(){
33449         if(this.refreshDelegate){
33450            this.loaded = false;
33451            this.refreshDelegate();
33452         }
33453     },
33454     
33455     /**
33456      * Destroys this panel
33457      */
33458     destroy : function(){
33459         this.el.removeAllListeners();
33460         var tempEl = document.createElement("span");
33461         tempEl.appendChild(this.el.dom);
33462         tempEl.innerHTML = "";
33463         this.el.remove();
33464         this.el = null;
33465     },
33466     
33467     /**
33468      * form - if the content panel contains a form - this is a reference to it.
33469      * @type {Roo.form.Form}
33470      */
33471     form : false,
33472     /**
33473      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
33474      *    This contains a reference to it.
33475      * @type {Roo.View}
33476      */
33477     view : false,
33478     
33479       /**
33480      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
33481      * <pre><code>
33482
33483 layout.addxtype({
33484        xtype : 'Form',
33485        items: [ .... ]
33486    }
33487 );
33488
33489 </code></pre>
33490      * @param {Object} cfg Xtype definition of item to add.
33491      */
33492     
33493     addxtype : function(cfg) {
33494         // add form..
33495         if (cfg.xtype.match(/^Form$/)) {
33496             
33497             var el;
33498             //if (this.footer) {
33499             //    el = this.footer.container.insertSibling(false, 'before');
33500             //} else {
33501                 el = this.el.createChild();
33502             //}
33503
33504             this.form = new  Roo.form.Form(cfg);
33505             
33506             
33507             if ( this.form.allItems.length) this.form.render(el.dom);
33508             return this.form;
33509         }
33510         // should only have one of theses..
33511         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
33512             // views..
33513             cfg.el = this.el.appendChild(document.createElement("div"));
33514             // factory?
33515             
33516             var ret = new Roo.factory(cfg);
33517             ret.render && ret.render(false, ''); // render blank..
33518             this.view = ret;
33519             return ret;
33520         }
33521         return false;
33522     }
33523 });
33524
33525 /**
33526  * @class Roo.GridPanel
33527  * @extends Roo.ContentPanel
33528  * @constructor
33529  * Create a new GridPanel.
33530  * @param {Roo.grid.Grid} grid The grid for this panel
33531  * @param {String/Object} config A string to set only the panel's title, or a config object
33532  */
33533 Roo.GridPanel = function(grid, config){
33534     
33535   
33536     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
33537         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
33538         
33539     this.wrapper.dom.appendChild(grid.getGridEl().dom);
33540     
33541     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
33542     
33543     if(this.toolbar){
33544         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
33545     }
33546     // xtype created footer. - not sure if will work as we normally have to render first..
33547     if (this.footer && !this.footer.el && this.footer.xtype) {
33548         
33549         this.footer.container = this.grid.getView().getFooterPanel(true);
33550         this.footer.dataSource = this.grid.dataSource;
33551         this.footer = Roo.factory(this.footer, Roo);
33552         
33553     }
33554     
33555     grid.monitorWindowResize = false; // turn off autosizing
33556     grid.autoHeight = false;
33557     grid.autoWidth = false;
33558     this.grid = grid;
33559     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
33560 };
33561
33562 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
33563     getId : function(){
33564         return this.grid.id;
33565     },
33566     
33567     /**
33568      * Returns the grid for this panel
33569      * @return {Roo.grid.Grid} 
33570      */
33571     getGrid : function(){
33572         return this.grid;    
33573     },
33574     
33575     setSize : function(width, height){
33576         if(!this.ignoreResize(width, height)){
33577             var grid = this.grid;
33578             var size = this.adjustForComponents(width, height);
33579             grid.getGridEl().setSize(size.width, size.height);
33580             grid.autoSize();
33581         }
33582     },
33583     
33584     beforeSlide : function(){
33585         this.grid.getView().scroller.clip();
33586     },
33587     
33588     afterSlide : function(){
33589         this.grid.getView().scroller.unclip();
33590     },
33591     
33592     destroy : function(){
33593         this.grid.destroy();
33594         delete this.grid;
33595         Roo.GridPanel.superclass.destroy.call(this); 
33596     }
33597 });
33598
33599
33600 /**
33601  * @class Roo.NestedLayoutPanel
33602  * @extends Roo.ContentPanel
33603  * @constructor
33604  * Create a new NestedLayoutPanel.
33605  * 
33606  * 
33607  * @param {Roo.BorderLayout} layout The layout for this panel
33608  * @param {String/Object} config A string to set only the title or a config object
33609  */
33610 Roo.NestedLayoutPanel = function(layout, config)
33611 {
33612     // construct with only one argument..
33613     /* FIXME - implement nicer consturctors
33614     if (layout.layout) {
33615         config = layout;
33616         layout = config.layout;
33617         delete config.layout;
33618     }
33619     if (layout.xtype && !layout.getEl) {
33620         // then layout needs constructing..
33621         layout = Roo.factory(layout, Roo);
33622     }
33623     */
33624     
33625     
33626     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
33627     
33628     layout.monitorWindowResize = false; // turn off autosizing
33629     this.layout = layout;
33630     this.layout.getEl().addClass("x-layout-nested-layout");
33631     
33632     
33633     
33634     
33635 };
33636
33637 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
33638
33639     setSize : function(width, height){
33640         if(!this.ignoreResize(width, height)){
33641             var size = this.adjustForComponents(width, height);
33642             var el = this.layout.getEl();
33643             el.setSize(size.width, size.height);
33644             var touch = el.dom.offsetWidth;
33645             this.layout.layout();
33646             // ie requires a double layout on the first pass
33647             if(Roo.isIE && !this.initialized){
33648                 this.initialized = true;
33649                 this.layout.layout();
33650             }
33651         }
33652     },
33653     
33654     // activate all subpanels if not currently active..
33655     
33656     setActiveState : function(active){
33657         this.active = active;
33658         if(!active){
33659             this.fireEvent("deactivate", this);
33660             return;
33661         }
33662         
33663         this.fireEvent("activate", this);
33664         // not sure if this should happen before or after..
33665         if (!this.layout) {
33666             return; // should not happen..
33667         }
33668         var reg = false;
33669         for (var r in this.layout.regions) {
33670             reg = this.layout.getRegion(r);
33671             if (reg.getActivePanel()) {
33672                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
33673                 reg.setActivePanel(reg.getActivePanel());
33674                 continue;
33675             }
33676             if (!reg.panels.length) {
33677                 continue;
33678             }
33679             reg.showPanel(reg.getPanel(0));
33680         }
33681         
33682         
33683         
33684         
33685     },
33686     
33687     /**
33688      * Returns the nested BorderLayout for this panel
33689      * @return {Roo.BorderLayout} 
33690      */
33691     getLayout : function(){
33692         return this.layout;
33693     },
33694     
33695      /**
33696      * Adds a xtype elements to the layout of the nested panel
33697      * <pre><code>
33698
33699 panel.addxtype({
33700        xtype : 'ContentPanel',
33701        region: 'west',
33702        items: [ .... ]
33703    }
33704 );
33705
33706 panel.addxtype({
33707         xtype : 'NestedLayoutPanel',
33708         region: 'west',
33709         layout: {
33710            center: { },
33711            west: { }   
33712         },
33713         items : [ ... list of content panels or nested layout panels.. ]
33714    }
33715 );
33716 </code></pre>
33717      * @param {Object} cfg Xtype definition of item to add.
33718      */
33719     addxtype : function(cfg) {
33720         return this.layout.addxtype(cfg);
33721     
33722     }
33723 });
33724
33725 Roo.ScrollPanel = function(el, config, content){
33726     config = config || {};
33727     config.fitToFrame = true;
33728     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
33729     
33730     this.el.dom.style.overflow = "hidden";
33731     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
33732     this.el.removeClass("x-layout-inactive-content");
33733     this.el.on("mousewheel", this.onWheel, this);
33734
33735     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
33736     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
33737     up.unselectable(); down.unselectable();
33738     up.on("click", this.scrollUp, this);
33739     down.on("click", this.scrollDown, this);
33740     up.addClassOnOver("x-scroller-btn-over");
33741     down.addClassOnOver("x-scroller-btn-over");
33742     up.addClassOnClick("x-scroller-btn-click");
33743     down.addClassOnClick("x-scroller-btn-click");
33744     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
33745
33746     this.resizeEl = this.el;
33747     this.el = wrap; this.up = up; this.down = down;
33748 };
33749
33750 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
33751     increment : 100,
33752     wheelIncrement : 5,
33753     scrollUp : function(){
33754         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
33755     },
33756
33757     scrollDown : function(){
33758         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
33759     },
33760
33761     afterScroll : function(){
33762         var el = this.resizeEl;
33763         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
33764         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
33765         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
33766     },
33767
33768     setSize : function(){
33769         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
33770         this.afterScroll();
33771     },
33772
33773     onWheel : function(e){
33774         var d = e.getWheelDelta();
33775         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
33776         this.afterScroll();
33777         e.stopEvent();
33778     },
33779
33780     setContent : function(content, loadScripts){
33781         this.resizeEl.update(content, loadScripts);
33782     }
33783
33784 });
33785
33786
33787
33788
33789
33790
33791
33792
33793
33794 /**
33795  * @class Roo.TreePanel
33796  * @extends Roo.ContentPanel
33797  * @constructor
33798  * Create a new TreePanel. - defaults to fit/scoll contents.
33799  * @param {String/Object} config A string to set only the panel's title, or a config object
33800  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
33801  */
33802 Roo.TreePanel = function(config){
33803     var el = config.el;
33804     var tree = config.tree;
33805     delete config.tree; 
33806     delete config.el; // hopefull!
33807     
33808     // wrapper for IE7 strict & safari scroll issue
33809     
33810     var treeEl = el.createChild();
33811     config.resizeEl = treeEl;
33812     
33813     
33814     
33815     Roo.TreePanel.superclass.constructor.call(this, el, config);
33816  
33817  
33818     this.tree = new Roo.tree.TreePanel(treeEl , tree);
33819     //console.log(tree);
33820     this.on('activate', function()
33821     {
33822         if (this.tree.rendered) {
33823             return;
33824         }
33825         //console.log('render tree');
33826         this.tree.render();
33827     });
33828     // this should not be needed.. - it's actually the 'el' that resizes?
33829     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
33830     
33831     //this.on('resize',  function (cp, w, h) {
33832     //        this.tree.innerCt.setWidth(w);
33833     //        this.tree.innerCt.setHeight(h);
33834     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
33835     //});
33836
33837         
33838     
33839 };
33840
33841 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
33842     fitToFrame : true,
33843     autoScroll : true
33844 });
33845
33846
33847
33848
33849
33850
33851
33852
33853
33854
33855
33856 /*
33857  * Based on:
33858  * Ext JS Library 1.1.1
33859  * Copyright(c) 2006-2007, Ext JS, LLC.
33860  *
33861  * Originally Released Under LGPL - original licence link has changed is not relivant.
33862  *
33863  * Fork - LGPL
33864  * <script type="text/javascript">
33865  */
33866  
33867
33868 /**
33869  * @class Roo.ReaderLayout
33870  * @extends Roo.BorderLayout
33871  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
33872  * center region containing two nested regions (a top one for a list view and one for item preview below),
33873  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
33874  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
33875  * expedites the setup of the overall layout and regions for this common application style.
33876  * Example:
33877  <pre><code>
33878 var reader = new Roo.ReaderLayout();
33879 var CP = Roo.ContentPanel;  // shortcut for adding
33880
33881 reader.beginUpdate();
33882 reader.add("north", new CP("north", "North"));
33883 reader.add("west", new CP("west", {title: "West"}));
33884 reader.add("east", new CP("east", {title: "East"}));
33885
33886 reader.regions.listView.add(new CP("listView", "List"));
33887 reader.regions.preview.add(new CP("preview", "Preview"));
33888 reader.endUpdate();
33889 </code></pre>
33890 * @constructor
33891 * Create a new ReaderLayout
33892 * @param {Object} config Configuration options
33893 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
33894 * document.body if omitted)
33895 */
33896 Roo.ReaderLayout = function(config, renderTo){
33897     var c = config || {size:{}};
33898     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
33899         north: c.north !== false ? Roo.apply({
33900             split:false,
33901             initialSize: 32,
33902             titlebar: false
33903         }, c.north) : false,
33904         west: c.west !== false ? Roo.apply({
33905             split:true,
33906             initialSize: 200,
33907             minSize: 175,
33908             maxSize: 400,
33909             titlebar: true,
33910             collapsible: true,
33911             animate: true,
33912             margins:{left:5,right:0,bottom:5,top:5},
33913             cmargins:{left:5,right:5,bottom:5,top:5}
33914         }, c.west) : false,
33915         east: c.east !== false ? Roo.apply({
33916             split:true,
33917             initialSize: 200,
33918             minSize: 175,
33919             maxSize: 400,
33920             titlebar: true,
33921             collapsible: true,
33922             animate: true,
33923             margins:{left:0,right:5,bottom:5,top:5},
33924             cmargins:{left:5,right:5,bottom:5,top:5}
33925         }, c.east) : false,
33926         center: Roo.apply({
33927             tabPosition: 'top',
33928             autoScroll:false,
33929             closeOnTab: true,
33930             titlebar:false,
33931             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
33932         }, c.center)
33933     });
33934
33935     this.el.addClass('x-reader');
33936
33937     this.beginUpdate();
33938
33939     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
33940         south: c.preview !== false ? Roo.apply({
33941             split:true,
33942             initialSize: 200,
33943             minSize: 100,
33944             autoScroll:true,
33945             collapsible:true,
33946             titlebar: true,
33947             cmargins:{top:5,left:0, right:0, bottom:0}
33948         }, c.preview) : false,
33949         center: Roo.apply({
33950             autoScroll:false,
33951             titlebar:false,
33952             minHeight:200
33953         }, c.listView)
33954     });
33955     this.add('center', new Roo.NestedLayoutPanel(inner,
33956             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
33957
33958     this.endUpdate();
33959
33960     this.regions.preview = inner.getRegion('south');
33961     this.regions.listView = inner.getRegion('center');
33962 };
33963
33964 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
33965  * Based on:
33966  * Ext JS Library 1.1.1
33967  * Copyright(c) 2006-2007, Ext JS, LLC.
33968  *
33969  * Originally Released Under LGPL - original licence link has changed is not relivant.
33970  *
33971  * Fork - LGPL
33972  * <script type="text/javascript">
33973  */
33974  
33975 /**
33976  * @class Roo.grid.Grid
33977  * @extends Roo.util.Observable
33978  * This class represents the primary interface of a component based grid control.
33979  * <br><br>Usage:<pre><code>
33980  var grid = new Roo.grid.Grid("my-container-id", {
33981      ds: myDataStore,
33982      cm: myColModel,
33983      selModel: mySelectionModel,
33984      autoSizeColumns: true,
33985      monitorWindowResize: false,
33986      trackMouseOver: true
33987  });
33988  // set any options
33989  grid.render();
33990  * </code></pre>
33991  * <b>Common Problems:</b><br/>
33992  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
33993  * element will correct this<br/>
33994  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
33995  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
33996  * are unpredictable.<br/>
33997  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
33998  * grid to calculate dimensions/offsets.<br/>
33999   * @constructor
34000  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
34001  * The container MUST have some type of size defined for the grid to fill. The container will be
34002  * automatically set to position relative if it isn't already.
34003  * @param {Object} config A config object that sets properties on this grid.
34004  */
34005 Roo.grid.Grid = function(container, config){
34006         // initialize the container
34007         this.container = Roo.get(container);
34008         this.container.update("");
34009         this.container.setStyle("overflow", "hidden");
34010     this.container.addClass('x-grid-container');
34011
34012     this.id = this.container.id;
34013
34014     Roo.apply(this, config);
34015     // check and correct shorthanded configs
34016     if(this.ds){
34017         this.dataSource = this.ds;
34018         delete this.ds;
34019     }
34020     if(this.cm){
34021         this.colModel = this.cm;
34022         delete this.cm;
34023     }
34024     if(this.sm){
34025         this.selModel = this.sm;
34026         delete this.sm;
34027     }
34028
34029     if (this.selModel) {
34030         this.selModel = Roo.factory(this.selModel, Roo.grid);
34031         this.sm = this.selModel;
34032         this.sm.xmodule = this.xmodule || false;
34033     }
34034     if (typeof(this.colModel.config) == 'undefined') {
34035         this.colModel = new Roo.grid.ColumnModel(this.colModel);
34036         this.cm = this.colModel;
34037         this.cm.xmodule = this.xmodule || false;
34038     }
34039     if (this.dataSource) {
34040         this.dataSource= Roo.factory(this.dataSource, Roo.data);
34041         this.ds = this.dataSource;
34042         this.ds.xmodule = this.xmodule || false;
34043          
34044     }
34045     
34046     
34047     
34048     if(this.width){
34049         this.container.setWidth(this.width);
34050     }
34051
34052     if(this.height){
34053         this.container.setHeight(this.height);
34054     }
34055     /** @private */
34056         this.addEvents({
34057         // raw events
34058         /**
34059          * @event click
34060          * The raw click event for the entire grid.
34061          * @param {Roo.EventObject} e
34062          */
34063         "click" : true,
34064         /**
34065          * @event dblclick
34066          * The raw dblclick event for the entire grid.
34067          * @param {Roo.EventObject} e
34068          */
34069         "dblclick" : true,
34070         /**
34071          * @event contextmenu
34072          * The raw contextmenu event for the entire grid.
34073          * @param {Roo.EventObject} e
34074          */
34075         "contextmenu" : true,
34076         /**
34077          * @event mousedown
34078          * The raw mousedown event for the entire grid.
34079          * @param {Roo.EventObject} e
34080          */
34081         "mousedown" : true,
34082         /**
34083          * @event mouseup
34084          * The raw mouseup event for the entire grid.
34085          * @param {Roo.EventObject} e
34086          */
34087         "mouseup" : true,
34088         /**
34089          * @event mouseover
34090          * The raw mouseover event for the entire grid.
34091          * @param {Roo.EventObject} e
34092          */
34093         "mouseover" : true,
34094         /**
34095          * @event mouseout
34096          * The raw mouseout event for the entire grid.
34097          * @param {Roo.EventObject} e
34098          */
34099         "mouseout" : true,
34100         /**
34101          * @event keypress
34102          * The raw keypress event for the entire grid.
34103          * @param {Roo.EventObject} e
34104          */
34105         "keypress" : true,
34106         /**
34107          * @event keydown
34108          * The raw keydown event for the entire grid.
34109          * @param {Roo.EventObject} e
34110          */
34111         "keydown" : true,
34112
34113         // custom events
34114
34115         /**
34116          * @event cellclick
34117          * Fires when a cell is clicked
34118          * @param {Grid} this
34119          * @param {Number} rowIndex
34120          * @param {Number} columnIndex
34121          * @param {Roo.EventObject} e
34122          */
34123         "cellclick" : true,
34124         /**
34125          * @event celldblclick
34126          * Fires when a cell is double clicked
34127          * @param {Grid} this
34128          * @param {Number} rowIndex
34129          * @param {Number} columnIndex
34130          * @param {Roo.EventObject} e
34131          */
34132         "celldblclick" : true,
34133         /**
34134          * @event rowclick
34135          * Fires when a row is clicked
34136          * @param {Grid} this
34137          * @param {Number} rowIndex
34138          * @param {Roo.EventObject} e
34139          */
34140         "rowclick" : true,
34141         /**
34142          * @event rowdblclick
34143          * Fires when a row is double clicked
34144          * @param {Grid} this
34145          * @param {Number} rowIndex
34146          * @param {Roo.EventObject} e
34147          */
34148         "rowdblclick" : true,
34149         /**
34150          * @event headerclick
34151          * Fires when a header is clicked
34152          * @param {Grid} this
34153          * @param {Number} columnIndex
34154          * @param {Roo.EventObject} e
34155          */
34156         "headerclick" : true,
34157         /**
34158          * @event headerdblclick
34159          * Fires when a header cell is double clicked
34160          * @param {Grid} this
34161          * @param {Number} columnIndex
34162          * @param {Roo.EventObject} e
34163          */
34164         "headerdblclick" : true,
34165         /**
34166          * @event rowcontextmenu
34167          * Fires when a row is right clicked
34168          * @param {Grid} this
34169          * @param {Number} rowIndex
34170          * @param {Roo.EventObject} e
34171          */
34172         "rowcontextmenu" : true,
34173         /**
34174          * @event cellcontextmenu
34175          * Fires when a cell is right clicked
34176          * @param {Grid} this
34177          * @param {Number} rowIndex
34178          * @param {Number} cellIndex
34179          * @param {Roo.EventObject} e
34180          */
34181          "cellcontextmenu" : true,
34182         /**
34183          * @event headercontextmenu
34184          * Fires when a header is right clicked
34185          * @param {Grid} this
34186          * @param {Number} columnIndex
34187          * @param {Roo.EventObject} e
34188          */
34189         "headercontextmenu" : true,
34190         /**
34191          * @event bodyscroll
34192          * Fires when the body element is scrolled
34193          * @param {Number} scrollLeft
34194          * @param {Number} scrollTop
34195          */
34196         "bodyscroll" : true,
34197         /**
34198          * @event columnresize
34199          * Fires when the user resizes a column
34200          * @param {Number} columnIndex
34201          * @param {Number} newSize
34202          */
34203         "columnresize" : true,
34204         /**
34205          * @event columnmove
34206          * Fires when the user moves a column
34207          * @param {Number} oldIndex
34208          * @param {Number} newIndex
34209          */
34210         "columnmove" : true,
34211         /**
34212          * @event startdrag
34213          * Fires when row(s) start being dragged
34214          * @param {Grid} this
34215          * @param {Roo.GridDD} dd The drag drop object
34216          * @param {event} e The raw browser event
34217          */
34218         "startdrag" : true,
34219         /**
34220          * @event enddrag
34221          * Fires when a drag operation is complete
34222          * @param {Grid} this
34223          * @param {Roo.GridDD} dd The drag drop object
34224          * @param {event} e The raw browser event
34225          */
34226         "enddrag" : true,
34227         /**
34228          * @event dragdrop
34229          * Fires when dragged row(s) are dropped on a valid DD target
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         "dragdrop" : true,
34236         /**
34237          * @event dragover
34238          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
34239          * @param {Grid} this
34240          * @param {Roo.GridDD} dd The drag drop object
34241          * @param {String} targetId The target drag drop object
34242          * @param {event} e The raw browser event
34243          */
34244         "dragover" : true,
34245         /**
34246          * @event dragenter
34247          *  Fires when the dragged row(s) first cross another DD target while being dragged
34248          * @param {Grid} this
34249          * @param {Roo.GridDD} dd The drag drop object
34250          * @param {String} targetId The target drag drop object
34251          * @param {event} e The raw browser event
34252          */
34253         "dragenter" : true,
34254         /**
34255          * @event dragout
34256          * Fires when the dragged row(s) leave another DD target while being dragged
34257          * @param {Grid} this
34258          * @param {Roo.GridDD} dd The drag drop object
34259          * @param {String} targetId The target drag drop object
34260          * @param {event} e The raw browser event
34261          */
34262         "dragout" : true,
34263         /**
34264          * @event rowclass
34265          * Fires when a row is rendered, so you can change add a style to it.
34266          * @param {GridView} gridview   The grid view
34267          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
34268          */
34269         'rowclass' : true,
34270
34271         /**
34272          * @event render
34273          * Fires when the grid is rendered
34274          * @param {Grid} grid
34275          */
34276         'render' : true
34277     });
34278
34279     Roo.grid.Grid.superclass.constructor.call(this);
34280 };
34281 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
34282     
34283     /**
34284      * @cfg {String} ddGroup - drag drop group.
34285      */
34286
34287     /**
34288      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
34289      */
34290     minColumnWidth : 25,
34291
34292     /**
34293      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
34294      * <b>on initial render.</b> It is more efficient to explicitly size the columns
34295      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
34296      */
34297     autoSizeColumns : false,
34298
34299     /**
34300      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
34301      */
34302     autoSizeHeaders : true,
34303
34304     /**
34305      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
34306      */
34307     monitorWindowResize : true,
34308
34309     /**
34310      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
34311      * rows measured to get a columns size. Default is 0 (all rows).
34312      */
34313     maxRowsToMeasure : 0,
34314
34315     /**
34316      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
34317      */
34318     trackMouseOver : true,
34319
34320     /**
34321     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
34322     */
34323     
34324     /**
34325     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
34326     */
34327     enableDragDrop : false,
34328     
34329     /**
34330     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
34331     */
34332     enableColumnMove : true,
34333     
34334     /**
34335     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
34336     */
34337     enableColumnHide : true,
34338     
34339     /**
34340     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
34341     */
34342     enableRowHeightSync : false,
34343     
34344     /**
34345     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
34346     */
34347     stripeRows : true,
34348     
34349     /**
34350     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
34351     */
34352     autoHeight : false,
34353
34354     /**
34355      * @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.
34356      */
34357     autoExpandColumn : false,
34358
34359     /**
34360     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
34361     * Default is 50.
34362     */
34363     autoExpandMin : 50,
34364
34365     /**
34366     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
34367     */
34368     autoExpandMax : 1000,
34369
34370     /**
34371     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
34372     */
34373     view : null,
34374
34375     /**
34376     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
34377     */
34378     loadMask : false,
34379     /**
34380     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
34381     */
34382     dropTarget: false,
34383     
34384    
34385     
34386     // private
34387     rendered : false,
34388
34389     /**
34390     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
34391     * of a fixed width. Default is false.
34392     */
34393     /**
34394     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
34395     */
34396     /**
34397      * Called once after all setup has been completed and the grid is ready to be rendered.
34398      * @return {Roo.grid.Grid} this
34399      */
34400     render : function()
34401     {
34402         var c = this.container;
34403         // try to detect autoHeight/width mode
34404         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
34405             this.autoHeight = true;
34406         }
34407         var view = this.getView();
34408         view.init(this);
34409
34410         c.on("click", this.onClick, this);
34411         c.on("dblclick", this.onDblClick, this);
34412         c.on("contextmenu", this.onContextMenu, this);
34413         c.on("keydown", this.onKeyDown, this);
34414
34415         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
34416
34417         this.getSelectionModel().init(this);
34418
34419         view.render();
34420
34421         if(this.loadMask){
34422             this.loadMask = new Roo.LoadMask(this.container,
34423                     Roo.apply({store:this.dataSource}, this.loadMask));
34424         }
34425         
34426         
34427         if (this.toolbar && this.toolbar.xtype) {
34428             this.toolbar.container = this.getView().getHeaderPanel(true);
34429             this.toolbar = new Roo.Toolbar(this.toolbar);
34430         }
34431         if (this.footer && this.footer.xtype) {
34432             this.footer.dataSource = this.getDataSource();
34433             this.footer.container = this.getView().getFooterPanel(true);
34434             this.footer = Roo.factory(this.footer, Roo);
34435         }
34436         if (this.dropTarget && this.dropTarget.xtype) {
34437             delete this.dropTarget.xtype;
34438             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
34439         }
34440         
34441         
34442         this.rendered = true;
34443         this.fireEvent('render', this);
34444         return this;
34445     },
34446
34447         /**
34448          * Reconfigures the grid to use a different Store and Column Model.
34449          * The View will be bound to the new objects and refreshed.
34450          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
34451          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
34452          */
34453     reconfigure : function(dataSource, colModel){
34454         if(this.loadMask){
34455             this.loadMask.destroy();
34456             this.loadMask = new Roo.LoadMask(this.container,
34457                     Roo.apply({store:dataSource}, this.loadMask));
34458         }
34459         this.view.bind(dataSource, colModel);
34460         this.dataSource = dataSource;
34461         this.colModel = colModel;
34462         this.view.refresh(true);
34463     },
34464
34465     // private
34466     onKeyDown : function(e){
34467         this.fireEvent("keydown", e);
34468     },
34469
34470     /**
34471      * Destroy this grid.
34472      * @param {Boolean} removeEl True to remove the element
34473      */
34474     destroy : function(removeEl, keepListeners){
34475         if(this.loadMask){
34476             this.loadMask.destroy();
34477         }
34478         var c = this.container;
34479         c.removeAllListeners();
34480         this.view.destroy();
34481         this.colModel.purgeListeners();
34482         if(!keepListeners){
34483             this.purgeListeners();
34484         }
34485         c.update("");
34486         if(removeEl === true){
34487             c.remove();
34488         }
34489     },
34490
34491     // private
34492     processEvent : function(name, e){
34493         this.fireEvent(name, e);
34494         var t = e.getTarget();
34495         var v = this.view;
34496         var header = v.findHeaderIndex(t);
34497         if(header !== false){
34498             this.fireEvent("header" + name, this, header, e);
34499         }else{
34500             var row = v.findRowIndex(t);
34501             var cell = v.findCellIndex(t);
34502             if(row !== false){
34503                 this.fireEvent("row" + name, this, row, e);
34504                 if(cell !== false){
34505                     this.fireEvent("cell" + name, this, row, cell, e);
34506                 }
34507             }
34508         }
34509     },
34510
34511     // private
34512     onClick : function(e){
34513         this.processEvent("click", e);
34514     },
34515
34516     // private
34517     onContextMenu : function(e, t){
34518         this.processEvent("contextmenu", e);
34519     },
34520
34521     // private
34522     onDblClick : function(e){
34523         this.processEvent("dblclick", e);
34524     },
34525
34526     // private
34527     walkCells : function(row, col, step, fn, scope){
34528         var cm = this.colModel, clen = cm.getColumnCount();
34529         var ds = this.dataSource, rlen = ds.getCount(), first = true;
34530         if(step < 0){
34531             if(col < 0){
34532                 row--;
34533                 first = false;
34534             }
34535             while(row >= 0){
34536                 if(!first){
34537                     col = clen-1;
34538                 }
34539                 first = false;
34540                 while(col >= 0){
34541                     if(fn.call(scope || this, row, col, cm) === true){
34542                         return [row, col];
34543                     }
34544                     col--;
34545                 }
34546                 row--;
34547             }
34548         } else {
34549             if(col >= clen){
34550                 row++;
34551                 first = false;
34552             }
34553             while(row < rlen){
34554                 if(!first){
34555                     col = 0;
34556                 }
34557                 first = false;
34558                 while(col < clen){
34559                     if(fn.call(scope || this, row, col, cm) === true){
34560                         return [row, col];
34561                     }
34562                     col++;
34563                 }
34564                 row++;
34565             }
34566         }
34567         return null;
34568     },
34569
34570     // private
34571     getSelections : function(){
34572         return this.selModel.getSelections();
34573     },
34574
34575     /**
34576      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
34577      * but if manual update is required this method will initiate it.
34578      */
34579     autoSize : function(){
34580         if(this.rendered){
34581             this.view.layout();
34582             if(this.view.adjustForScroll){
34583                 this.view.adjustForScroll();
34584             }
34585         }
34586     },
34587
34588     /**
34589      * Returns the grid's underlying element.
34590      * @return {Element} The element
34591      */
34592     getGridEl : function(){
34593         return this.container;
34594     },
34595
34596     // private for compatibility, overridden by editor grid
34597     stopEditing : function(){},
34598
34599     /**
34600      * Returns the grid's SelectionModel.
34601      * @return {SelectionModel}
34602      */
34603     getSelectionModel : function(){
34604         if(!this.selModel){
34605             this.selModel = new Roo.grid.RowSelectionModel();
34606         }
34607         return this.selModel;
34608     },
34609
34610     /**
34611      * Returns the grid's DataSource.
34612      * @return {DataSource}
34613      */
34614     getDataSource : function(){
34615         return this.dataSource;
34616     },
34617
34618     /**
34619      * Returns the grid's ColumnModel.
34620      * @return {ColumnModel}
34621      */
34622     getColumnModel : function(){
34623         return this.colModel;
34624     },
34625
34626     /**
34627      * Returns the grid's GridView object.
34628      * @return {GridView}
34629      */
34630     getView : function(){
34631         if(!this.view){
34632             this.view = new Roo.grid.GridView(this.viewConfig);
34633         }
34634         return this.view;
34635     },
34636     /**
34637      * Called to get grid's drag proxy text, by default returns this.ddText.
34638      * @return {String}
34639      */
34640     getDragDropText : function(){
34641         var count = this.selModel.getCount();
34642         return String.format(this.ddText, count, count == 1 ? '' : 's');
34643     }
34644 });
34645 /**
34646  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
34647  * %0 is replaced with the number of selected rows.
34648  * @type String
34649  */
34650 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
34651  * Based on:
34652  * Ext JS Library 1.1.1
34653  * Copyright(c) 2006-2007, Ext JS, LLC.
34654  *
34655  * Originally Released Under LGPL - original licence link has changed is not relivant.
34656  *
34657  * Fork - LGPL
34658  * <script type="text/javascript">
34659  */
34660  
34661 Roo.grid.AbstractGridView = function(){
34662         this.grid = null;
34663         
34664         this.events = {
34665             "beforerowremoved" : true,
34666             "beforerowsinserted" : true,
34667             "beforerefresh" : true,
34668             "rowremoved" : true,
34669             "rowsinserted" : true,
34670             "rowupdated" : true,
34671             "refresh" : true
34672         };
34673     Roo.grid.AbstractGridView.superclass.constructor.call(this);
34674 };
34675
34676 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
34677     rowClass : "x-grid-row",
34678     cellClass : "x-grid-cell",
34679     tdClass : "x-grid-td",
34680     hdClass : "x-grid-hd",
34681     splitClass : "x-grid-hd-split",
34682     
34683         init: function(grid){
34684         this.grid = grid;
34685                 var cid = this.grid.getGridEl().id;
34686         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
34687         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
34688         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
34689         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
34690         },
34691         
34692         getColumnRenderers : function(){
34693         var renderers = [];
34694         var cm = this.grid.colModel;
34695         var colCount = cm.getColumnCount();
34696         for(var i = 0; i < colCount; i++){
34697             renderers[i] = cm.getRenderer(i);
34698         }
34699         return renderers;
34700     },
34701     
34702     getColumnIds : function(){
34703         var ids = [];
34704         var cm = this.grid.colModel;
34705         var colCount = cm.getColumnCount();
34706         for(var i = 0; i < colCount; i++){
34707             ids[i] = cm.getColumnId(i);
34708         }
34709         return ids;
34710     },
34711     
34712     getDataIndexes : function(){
34713         if(!this.indexMap){
34714             this.indexMap = this.buildIndexMap();
34715         }
34716         return this.indexMap.colToData;
34717     },
34718     
34719     getColumnIndexByDataIndex : function(dataIndex){
34720         if(!this.indexMap){
34721             this.indexMap = this.buildIndexMap();
34722         }
34723         return this.indexMap.dataToCol[dataIndex];
34724     },
34725     
34726     /**
34727      * Set a css style for a column dynamically. 
34728      * @param {Number} colIndex The index of the column
34729      * @param {String} name The css property name
34730      * @param {String} value The css value
34731      */
34732     setCSSStyle : function(colIndex, name, value){
34733         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
34734         Roo.util.CSS.updateRule(selector, name, value);
34735     },
34736     
34737     generateRules : function(cm){
34738         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
34739         Roo.util.CSS.removeStyleSheet(rulesId);
34740         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34741             var cid = cm.getColumnId(i);
34742             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
34743                          this.tdSelector, cid, " {\n}\n",
34744                          this.hdSelector, cid, " {\n}\n",
34745                          this.splitSelector, cid, " {\n}\n");
34746         }
34747         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
34748     }
34749 });/*
34750  * Based on:
34751  * Ext JS Library 1.1.1
34752  * Copyright(c) 2006-2007, Ext JS, LLC.
34753  *
34754  * Originally Released Under LGPL - original licence link has changed is not relivant.
34755  *
34756  * Fork - LGPL
34757  * <script type="text/javascript">
34758  */
34759
34760 // private
34761 // This is a support class used internally by the Grid components
34762 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
34763     this.grid = grid;
34764     this.view = grid.getView();
34765     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
34766     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
34767     if(hd2){
34768         this.setHandleElId(Roo.id(hd));
34769         this.setOuterHandleElId(Roo.id(hd2));
34770     }
34771     this.scroll = false;
34772 };
34773 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
34774     maxDragWidth: 120,
34775     getDragData : function(e){
34776         var t = Roo.lib.Event.getTarget(e);
34777         var h = this.view.findHeaderCell(t);
34778         if(h){
34779             return {ddel: h.firstChild, header:h};
34780         }
34781         return false;
34782     },
34783
34784     onInitDrag : function(e){
34785         this.view.headersDisabled = true;
34786         var clone = this.dragData.ddel.cloneNode(true);
34787         clone.id = Roo.id();
34788         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
34789         this.proxy.update(clone);
34790         return true;
34791     },
34792
34793     afterValidDrop : function(){
34794         var v = this.view;
34795         setTimeout(function(){
34796             v.headersDisabled = false;
34797         }, 50);
34798     },
34799
34800     afterInvalidDrop : function(){
34801         var v = this.view;
34802         setTimeout(function(){
34803             v.headersDisabled = false;
34804         }, 50);
34805     }
34806 });
34807 /*
34808  * Based on:
34809  * Ext JS Library 1.1.1
34810  * Copyright(c) 2006-2007, Ext JS, LLC.
34811  *
34812  * Originally Released Under LGPL - original licence link has changed is not relivant.
34813  *
34814  * Fork - LGPL
34815  * <script type="text/javascript">
34816  */
34817 // private
34818 // This is a support class used internally by the Grid components
34819 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
34820     this.grid = grid;
34821     this.view = grid.getView();
34822     // split the proxies so they don't interfere with mouse events
34823     this.proxyTop = Roo.DomHelper.append(document.body, {
34824         cls:"col-move-top", html:"&#160;"
34825     }, true);
34826     this.proxyBottom = Roo.DomHelper.append(document.body, {
34827         cls:"col-move-bottom", html:"&#160;"
34828     }, true);
34829     this.proxyTop.hide = this.proxyBottom.hide = function(){
34830         this.setLeftTop(-100,-100);
34831         this.setStyle("visibility", "hidden");
34832     };
34833     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
34834     // temporarily disabled
34835     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
34836     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
34837 };
34838 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
34839     proxyOffsets : [-4, -9],
34840     fly: Roo.Element.fly,
34841
34842     getTargetFromEvent : function(e){
34843         var t = Roo.lib.Event.getTarget(e);
34844         var cindex = this.view.findCellIndex(t);
34845         if(cindex !== false){
34846             return this.view.getHeaderCell(cindex);
34847         }
34848         return null;
34849     },
34850
34851     nextVisible : function(h){
34852         var v = this.view, cm = this.grid.colModel;
34853         h = h.nextSibling;
34854         while(h){
34855             if(!cm.isHidden(v.getCellIndex(h))){
34856                 return h;
34857             }
34858             h = h.nextSibling;
34859         }
34860         return null;
34861     },
34862
34863     prevVisible : function(h){
34864         var v = this.view, cm = this.grid.colModel;
34865         h = h.prevSibling;
34866         while(h){
34867             if(!cm.isHidden(v.getCellIndex(h))){
34868                 return h;
34869             }
34870             h = h.prevSibling;
34871         }
34872         return null;
34873     },
34874
34875     positionIndicator : function(h, n, e){
34876         var x = Roo.lib.Event.getPageX(e);
34877         var r = Roo.lib.Dom.getRegion(n.firstChild);
34878         var px, pt, py = r.top + this.proxyOffsets[1];
34879         if((r.right - x) <= (r.right-r.left)/2){
34880             px = r.right+this.view.borderWidth;
34881             pt = "after";
34882         }else{
34883             px = r.left;
34884             pt = "before";
34885         }
34886         var oldIndex = this.view.getCellIndex(h);
34887         var newIndex = this.view.getCellIndex(n);
34888
34889         if(this.grid.colModel.isFixed(newIndex)){
34890             return false;
34891         }
34892
34893         var locked = this.grid.colModel.isLocked(newIndex);
34894
34895         if(pt == "after"){
34896             newIndex++;
34897         }
34898         if(oldIndex < newIndex){
34899             newIndex--;
34900         }
34901         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
34902             return false;
34903         }
34904         px +=  this.proxyOffsets[0];
34905         this.proxyTop.setLeftTop(px, py);
34906         this.proxyTop.show();
34907         if(!this.bottomOffset){
34908             this.bottomOffset = this.view.mainHd.getHeight();
34909         }
34910         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
34911         this.proxyBottom.show();
34912         return pt;
34913     },
34914
34915     onNodeEnter : function(n, dd, e, data){
34916         if(data.header != n){
34917             this.positionIndicator(data.header, n, e);
34918         }
34919     },
34920
34921     onNodeOver : function(n, dd, e, data){
34922         var result = false;
34923         if(data.header != n){
34924             result = this.positionIndicator(data.header, n, e);
34925         }
34926         if(!result){
34927             this.proxyTop.hide();
34928             this.proxyBottom.hide();
34929         }
34930         return result ? this.dropAllowed : this.dropNotAllowed;
34931     },
34932
34933     onNodeOut : function(n, dd, e, data){
34934         this.proxyTop.hide();
34935         this.proxyBottom.hide();
34936     },
34937
34938     onNodeDrop : function(n, dd, e, data){
34939         var h = data.header;
34940         if(h != n){
34941             var cm = this.grid.colModel;
34942             var x = Roo.lib.Event.getPageX(e);
34943             var r = Roo.lib.Dom.getRegion(n.firstChild);
34944             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
34945             var oldIndex = this.view.getCellIndex(h);
34946             var newIndex = this.view.getCellIndex(n);
34947             var locked = cm.isLocked(newIndex);
34948             if(pt == "after"){
34949                 newIndex++;
34950             }
34951             if(oldIndex < newIndex){
34952                 newIndex--;
34953             }
34954             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
34955                 return false;
34956             }
34957             cm.setLocked(oldIndex, locked, true);
34958             cm.moveColumn(oldIndex, newIndex);
34959             this.grid.fireEvent("columnmove", oldIndex, newIndex);
34960             return true;
34961         }
34962         return false;
34963     }
34964 });
34965 /*
34966  * Based on:
34967  * Ext JS Library 1.1.1
34968  * Copyright(c) 2006-2007, Ext JS, LLC.
34969  *
34970  * Originally Released Under LGPL - original licence link has changed is not relivant.
34971  *
34972  * Fork - LGPL
34973  * <script type="text/javascript">
34974  */
34975   
34976 /**
34977  * @class Roo.grid.GridView
34978  * @extends Roo.util.Observable
34979  *
34980  * @constructor
34981  * @param {Object} config
34982  */
34983 Roo.grid.GridView = function(config){
34984     Roo.grid.GridView.superclass.constructor.call(this);
34985     this.el = null;
34986
34987     Roo.apply(this, config);
34988 };
34989
34990 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
34991
34992     
34993     rowClass : "x-grid-row",
34994
34995     cellClass : "x-grid-col",
34996
34997     tdClass : "x-grid-td",
34998
34999     hdClass : "x-grid-hd",
35000
35001     splitClass : "x-grid-split",
35002
35003     sortClasses : ["sort-asc", "sort-desc"],
35004
35005     enableMoveAnim : false,
35006
35007     hlColor: "C3DAF9",
35008
35009     dh : Roo.DomHelper,
35010
35011     fly : Roo.Element.fly,
35012
35013     css : Roo.util.CSS,
35014
35015     borderWidth: 1,
35016
35017     splitOffset: 3,
35018
35019     scrollIncrement : 22,
35020
35021     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
35022
35023     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
35024
35025     bind : function(ds, cm){
35026         if(this.ds){
35027             this.ds.un("load", this.onLoad, this);
35028             this.ds.un("datachanged", this.onDataChange, this);
35029             this.ds.un("add", this.onAdd, this);
35030             this.ds.un("remove", this.onRemove, this);
35031             this.ds.un("update", this.onUpdate, this);
35032             this.ds.un("clear", this.onClear, this);
35033         }
35034         if(ds){
35035             ds.on("load", this.onLoad, this);
35036             ds.on("datachanged", this.onDataChange, this);
35037             ds.on("add", this.onAdd, this);
35038             ds.on("remove", this.onRemove, this);
35039             ds.on("update", this.onUpdate, this);
35040             ds.on("clear", this.onClear, this);
35041         }
35042         this.ds = ds;
35043
35044         if(this.cm){
35045             this.cm.un("widthchange", this.onColWidthChange, this);
35046             this.cm.un("headerchange", this.onHeaderChange, this);
35047             this.cm.un("hiddenchange", this.onHiddenChange, this);
35048             this.cm.un("columnmoved", this.onColumnMove, this);
35049             this.cm.un("columnlockchange", this.onColumnLock, this);
35050         }
35051         if(cm){
35052             this.generateRules(cm);
35053             cm.on("widthchange", this.onColWidthChange, this);
35054             cm.on("headerchange", this.onHeaderChange, this);
35055             cm.on("hiddenchange", this.onHiddenChange, this);
35056             cm.on("columnmoved", this.onColumnMove, this);
35057             cm.on("columnlockchange", this.onColumnLock, this);
35058         }
35059         this.cm = cm;
35060     },
35061
35062     init: function(grid){
35063         Roo.grid.GridView.superclass.init.call(this, grid);
35064
35065         this.bind(grid.dataSource, grid.colModel);
35066
35067         grid.on("headerclick", this.handleHeaderClick, this);
35068
35069         if(grid.trackMouseOver){
35070             grid.on("mouseover", this.onRowOver, this);
35071             grid.on("mouseout", this.onRowOut, this);
35072         }
35073         grid.cancelTextSelection = function(){};
35074         this.gridId = grid.id;
35075
35076         var tpls = this.templates || {};
35077
35078         if(!tpls.master){
35079             tpls.master = new Roo.Template(
35080                '<div class="x-grid" hidefocus="true">',
35081                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
35082                   '<div class="x-grid-topbar"></div>',
35083                   '<div class="x-grid-scroller"><div></div></div>',
35084                   '<div class="x-grid-locked">',
35085                       '<div class="x-grid-header">{lockedHeader}</div>',
35086                       '<div class="x-grid-body">{lockedBody}</div>',
35087                   "</div>",
35088                   '<div class="x-grid-viewport">',
35089                       '<div class="x-grid-header">{header}</div>',
35090                       '<div class="x-grid-body">{body}</div>',
35091                   "</div>",
35092                   '<div class="x-grid-bottombar"></div>',
35093                  
35094                   '<div class="x-grid-resize-proxy">&#160;</div>',
35095                "</div>"
35096             );
35097             tpls.master.disableformats = true;
35098         }
35099
35100         if(!tpls.header){
35101             tpls.header = new Roo.Template(
35102                '<table border="0" cellspacing="0" cellpadding="0">',
35103                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
35104                "</table>{splits}"
35105             );
35106             tpls.header.disableformats = true;
35107         }
35108         tpls.header.compile();
35109
35110         if(!tpls.hcell){
35111             tpls.hcell = new Roo.Template(
35112                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
35113                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
35114                 "</div></td>"
35115              );
35116              tpls.hcell.disableFormats = true;
35117         }
35118         tpls.hcell.compile();
35119
35120         if(!tpls.hsplit){
35121             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
35122             tpls.hsplit.disableFormats = true;
35123         }
35124         tpls.hsplit.compile();
35125
35126         if(!tpls.body){
35127             tpls.body = new Roo.Template(
35128                '<table border="0" cellspacing="0" cellpadding="0">',
35129                "<tbody>{rows}</tbody>",
35130                "</table>"
35131             );
35132             tpls.body.disableFormats = true;
35133         }
35134         tpls.body.compile();
35135
35136         if(!tpls.row){
35137             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
35138             tpls.row.disableFormats = true;
35139         }
35140         tpls.row.compile();
35141
35142         if(!tpls.cell){
35143             tpls.cell = new Roo.Template(
35144                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
35145                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
35146                 "</td>"
35147             );
35148             tpls.cell.disableFormats = true;
35149         }
35150         tpls.cell.compile();
35151
35152         this.templates = tpls;
35153     },
35154
35155     // remap these for backwards compat
35156     onColWidthChange : function(){
35157         this.updateColumns.apply(this, arguments);
35158     },
35159     onHeaderChange : function(){
35160         this.updateHeaders.apply(this, arguments);
35161     }, 
35162     onHiddenChange : function(){
35163         this.handleHiddenChange.apply(this, arguments);
35164     },
35165     onColumnMove : function(){
35166         this.handleColumnMove.apply(this, arguments);
35167     },
35168     onColumnLock : function(){
35169         this.handleLockChange.apply(this, arguments);
35170     },
35171
35172     onDataChange : function(){
35173         this.refresh();
35174         this.updateHeaderSortState();
35175     },
35176
35177     onClear : function(){
35178         this.refresh();
35179     },
35180
35181     onUpdate : function(ds, record){
35182         this.refreshRow(record);
35183     },
35184
35185     refreshRow : function(record){
35186         var ds = this.ds, index;
35187         if(typeof record == 'number'){
35188             index = record;
35189             record = ds.getAt(index);
35190         }else{
35191             index = ds.indexOf(record);
35192         }
35193         this.insertRows(ds, index, index, true);
35194         this.onRemove(ds, record, index+1, true);
35195         this.syncRowHeights(index, index);
35196         this.layout();
35197         this.fireEvent("rowupdated", this, index, record);
35198     },
35199
35200     onAdd : function(ds, records, index){
35201         this.insertRows(ds, index, index + (records.length-1));
35202     },
35203
35204     onRemove : function(ds, record, index, isUpdate){
35205         if(isUpdate !== true){
35206             this.fireEvent("beforerowremoved", this, index, record);
35207         }
35208         var bt = this.getBodyTable(), lt = this.getLockedTable();
35209         if(bt.rows[index]){
35210             bt.firstChild.removeChild(bt.rows[index]);
35211         }
35212         if(lt.rows[index]){
35213             lt.firstChild.removeChild(lt.rows[index]);
35214         }
35215         if(isUpdate !== true){
35216             this.stripeRows(index);
35217             this.syncRowHeights(index, index);
35218             this.layout();
35219             this.fireEvent("rowremoved", this, index, record);
35220         }
35221     },
35222
35223     onLoad : function(){
35224         this.scrollToTop();
35225     },
35226
35227     /**
35228      * Scrolls the grid to the top
35229      */
35230     scrollToTop : function(){
35231         if(this.scroller){
35232             this.scroller.dom.scrollTop = 0;
35233             this.syncScroll();
35234         }
35235     },
35236
35237     /**
35238      * Gets a panel in the header of the grid that can be used for toolbars etc.
35239      * After modifying the contents of this panel a call to grid.autoSize() may be
35240      * required to register any changes in size.
35241      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
35242      * @return Roo.Element
35243      */
35244     getHeaderPanel : function(doShow){
35245         if(doShow){
35246             this.headerPanel.show();
35247         }
35248         return this.headerPanel;
35249     },
35250
35251     /**
35252      * Gets a panel in the footer of the grid that can be used for toolbars etc.
35253      * After modifying the contents of this panel a call to grid.autoSize() may be
35254      * required to register any changes in size.
35255      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
35256      * @return Roo.Element
35257      */
35258     getFooterPanel : function(doShow){
35259         if(doShow){
35260             this.footerPanel.show();
35261         }
35262         return this.footerPanel;
35263     },
35264
35265     initElements : function(){
35266         var E = Roo.Element;
35267         var el = this.grid.getGridEl().dom.firstChild;
35268         var cs = el.childNodes;
35269
35270         this.el = new E(el);
35271         
35272          this.focusEl = new E(el.firstChild);
35273         this.focusEl.swallowEvent("click", true);
35274         
35275         this.headerPanel = new E(cs[1]);
35276         this.headerPanel.enableDisplayMode("block");
35277
35278         this.scroller = new E(cs[2]);
35279         this.scrollSizer = new E(this.scroller.dom.firstChild);
35280
35281         this.lockedWrap = new E(cs[3]);
35282         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
35283         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
35284
35285         this.mainWrap = new E(cs[4]);
35286         this.mainHd = new E(this.mainWrap.dom.firstChild);
35287         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
35288
35289         this.footerPanel = new E(cs[5]);
35290         this.footerPanel.enableDisplayMode("block");
35291
35292         this.resizeProxy = new E(cs[6]);
35293
35294         this.headerSelector = String.format(
35295            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
35296            this.lockedHd.id, this.mainHd.id
35297         );
35298
35299         this.splitterSelector = String.format(
35300            '#{0} div.x-grid-split, #{1} div.x-grid-split',
35301            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
35302         );
35303     },
35304     idToCssName : function(s)
35305     {
35306         return s.replace(/[^a-z0-9]+/ig, '-');
35307     },
35308
35309     getHeaderCell : function(index){
35310         return Roo.DomQuery.select(this.headerSelector)[index];
35311     },
35312
35313     getHeaderCellMeasure : function(index){
35314         return this.getHeaderCell(index).firstChild;
35315     },
35316
35317     getHeaderCellText : function(index){
35318         return this.getHeaderCell(index).firstChild.firstChild;
35319     },
35320
35321     getLockedTable : function(){
35322         return this.lockedBody.dom.firstChild;
35323     },
35324
35325     getBodyTable : function(){
35326         return this.mainBody.dom.firstChild;
35327     },
35328
35329     getLockedRow : function(index){
35330         return this.getLockedTable().rows[index];
35331     },
35332
35333     getRow : function(index){
35334         return this.getBodyTable().rows[index];
35335     },
35336
35337     getRowComposite : function(index){
35338         if(!this.rowEl){
35339             this.rowEl = new Roo.CompositeElementLite();
35340         }
35341         var els = [], lrow, mrow;
35342         if(lrow = this.getLockedRow(index)){
35343             els.push(lrow);
35344         }
35345         if(mrow = this.getRow(index)){
35346             els.push(mrow);
35347         }
35348         this.rowEl.elements = els;
35349         return this.rowEl;
35350     },
35351     /**
35352      * Gets the 'td' of the cell
35353      * 
35354      * @param {Integer} rowIndex row to select
35355      * @param {Integer} colIndex column to select
35356      * 
35357      * @return {Object} 
35358      */
35359     getCell : function(rowIndex, colIndex){
35360         var locked = this.cm.getLockedCount();
35361         var source;
35362         if(colIndex < locked){
35363             source = this.lockedBody.dom.firstChild;
35364         }else{
35365             source = this.mainBody.dom.firstChild;
35366             colIndex -= locked;
35367         }
35368         return source.rows[rowIndex].childNodes[colIndex];
35369     },
35370
35371     getCellText : function(rowIndex, colIndex){
35372         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
35373     },
35374
35375     getCellBox : function(cell){
35376         var b = this.fly(cell).getBox();
35377         if(Roo.isOpera){ // opera fails to report the Y
35378             b.y = cell.offsetTop + this.mainBody.getY();
35379         }
35380         return b;
35381     },
35382
35383     getCellIndex : function(cell){
35384         var id = String(cell.className).match(this.cellRE);
35385         if(id){
35386             return parseInt(id[1], 10);
35387         }
35388         return 0;
35389     },
35390
35391     findHeaderIndex : function(n){
35392         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
35393         return r ? this.getCellIndex(r) : false;
35394     },
35395
35396     findHeaderCell : function(n){
35397         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
35398         return r ? r : false;
35399     },
35400
35401     findRowIndex : function(n){
35402         if(!n){
35403             return false;
35404         }
35405         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
35406         return r ? r.rowIndex : false;
35407     },
35408
35409     findCellIndex : function(node){
35410         var stop = this.el.dom;
35411         while(node && node != stop){
35412             if(this.findRE.test(node.className)){
35413                 return this.getCellIndex(node);
35414             }
35415             node = node.parentNode;
35416         }
35417         return false;
35418     },
35419
35420     getColumnId : function(index){
35421         return this.cm.getColumnId(index);
35422     },
35423
35424     getSplitters : function()
35425     {
35426         if(this.splitterSelector){
35427            return Roo.DomQuery.select(this.splitterSelector);
35428         }else{
35429             return null;
35430       }
35431     },
35432
35433     getSplitter : function(index){
35434         return this.getSplitters()[index];
35435     },
35436
35437     onRowOver : function(e, t){
35438         var row;
35439         if((row = this.findRowIndex(t)) !== false){
35440             this.getRowComposite(row).addClass("x-grid-row-over");
35441         }
35442     },
35443
35444     onRowOut : function(e, t){
35445         var row;
35446         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
35447             this.getRowComposite(row).removeClass("x-grid-row-over");
35448         }
35449     },
35450
35451     renderHeaders : function(){
35452         var cm = this.cm;
35453         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
35454         var cb = [], lb = [], sb = [], lsb = [], p = {};
35455         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35456             p.cellId = "x-grid-hd-0-" + i;
35457             p.splitId = "x-grid-csplit-0-" + i;
35458             p.id = cm.getColumnId(i);
35459             p.title = cm.getColumnTooltip(i) || "";
35460             p.value = cm.getColumnHeader(i) || "";
35461             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
35462             if(!cm.isLocked(i)){
35463                 cb[cb.length] = ct.apply(p);
35464                 sb[sb.length] = st.apply(p);
35465             }else{
35466                 lb[lb.length] = ct.apply(p);
35467                 lsb[lsb.length] = st.apply(p);
35468             }
35469         }
35470         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
35471                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
35472     },
35473
35474     updateHeaders : function(){
35475         var html = this.renderHeaders();
35476         this.lockedHd.update(html[0]);
35477         this.mainHd.update(html[1]);
35478     },
35479
35480     /**
35481      * Focuses the specified row.
35482      * @param {Number} row The row index
35483      */
35484     focusRow : function(row)
35485     {
35486         //Roo.log('GridView.focusRow');
35487         var x = this.scroller.dom.scrollLeft;
35488         this.focusCell(row, 0, false);
35489         this.scroller.dom.scrollLeft = x;
35490     },
35491
35492     /**
35493      * Focuses the specified cell.
35494      * @param {Number} row The row index
35495      * @param {Number} col The column index
35496      * @param {Boolean} hscroll false to disable horizontal scrolling
35497      */
35498     focusCell : function(row, col, hscroll)
35499     {
35500         //Roo.log('GridView.focusCell');
35501         var el = this.ensureVisible(row, col, hscroll);
35502         this.focusEl.alignTo(el, "tl-tl");
35503         if(Roo.isGecko){
35504             this.focusEl.focus();
35505         }else{
35506             this.focusEl.focus.defer(1, this.focusEl);
35507         }
35508     },
35509
35510     /**
35511      * Scrolls the specified cell into view
35512      * @param {Number} row The row index
35513      * @param {Number} col The column index
35514      * @param {Boolean} hscroll false to disable horizontal scrolling
35515      */
35516     ensureVisible : function(row, col, hscroll)
35517     {
35518         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
35519         //return null; //disable for testing.
35520         if(typeof row != "number"){
35521             row = row.rowIndex;
35522         }
35523         if(row < 0 && row >= this.ds.getCount()){
35524             return  null;
35525         }
35526         col = (col !== undefined ? col : 0);
35527         var cm = this.grid.colModel;
35528         while(cm.isHidden(col)){
35529             col++;
35530         }
35531
35532         var el = this.getCell(row, col);
35533         if(!el){
35534             return null;
35535         }
35536         var c = this.scroller.dom;
35537
35538         var ctop = parseInt(el.offsetTop, 10);
35539         var cleft = parseInt(el.offsetLeft, 10);
35540         var cbot = ctop + el.offsetHeight;
35541         var cright = cleft + el.offsetWidth;
35542         
35543         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
35544         var stop = parseInt(c.scrollTop, 10);
35545         var sleft = parseInt(c.scrollLeft, 10);
35546         var sbot = stop + ch;
35547         var sright = sleft + c.clientWidth;
35548         /*
35549         Roo.log('GridView.ensureVisible:' +
35550                 ' ctop:' + ctop +
35551                 ' c.clientHeight:' + c.clientHeight +
35552                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
35553                 ' stop:' + stop +
35554                 ' cbot:' + cbot +
35555                 ' sbot:' + sbot +
35556                 ' ch:' + ch  
35557                 );
35558         */
35559         if(ctop < stop){
35560              c.scrollTop = ctop;
35561             //Roo.log("set scrolltop to ctop DISABLE?");
35562         }else if(cbot > sbot){
35563             //Roo.log("set scrolltop to cbot-ch");
35564             c.scrollTop = cbot-ch;
35565         }
35566         
35567         if(hscroll !== false){
35568             if(cleft < sleft){
35569                 c.scrollLeft = cleft;
35570             }else if(cright > sright){
35571                 c.scrollLeft = cright-c.clientWidth;
35572             }
35573         }
35574          
35575         return el;
35576     },
35577
35578     updateColumns : function(){
35579         this.grid.stopEditing();
35580         var cm = this.grid.colModel, colIds = this.getColumnIds();
35581         //var totalWidth = cm.getTotalWidth();
35582         var pos = 0;
35583         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35584             //if(cm.isHidden(i)) continue;
35585             var w = cm.getColumnWidth(i);
35586             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
35587             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
35588         }
35589         this.updateSplitters();
35590     },
35591
35592     generateRules : function(cm){
35593         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
35594         Roo.util.CSS.removeStyleSheet(rulesId);
35595         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35596             var cid = cm.getColumnId(i);
35597             var align = '';
35598             if(cm.config[i].align){
35599                 align = 'text-align:'+cm.config[i].align+';';
35600             }
35601             var hidden = '';
35602             if(cm.isHidden(i)){
35603                 hidden = 'display:none;';
35604             }
35605             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
35606             ruleBuf.push(
35607                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
35608                     this.hdSelector, cid, " {\n", align, width, "}\n",
35609                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
35610                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
35611         }
35612         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
35613     },
35614
35615     updateSplitters : function(){
35616         var cm = this.cm, s = this.getSplitters();
35617         if(s){ // splitters not created yet
35618             var pos = 0, locked = true;
35619             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35620                 if(cm.isHidden(i)) continue;
35621                 var w = cm.getColumnWidth(i); // make sure it's a number
35622                 if(!cm.isLocked(i) && locked){
35623                     pos = 0;
35624                     locked = false;
35625                 }
35626                 pos += w;
35627                 s[i].style.left = (pos-this.splitOffset) + "px";
35628             }
35629         }
35630     },
35631
35632     handleHiddenChange : function(colModel, colIndex, hidden){
35633         if(hidden){
35634             this.hideColumn(colIndex);
35635         }else{
35636             this.unhideColumn(colIndex);
35637         }
35638     },
35639
35640     hideColumn : function(colIndex){
35641         var cid = this.getColumnId(colIndex);
35642         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
35643         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
35644         if(Roo.isSafari){
35645             this.updateHeaders();
35646         }
35647         this.updateSplitters();
35648         this.layout();
35649     },
35650
35651     unhideColumn : function(colIndex){
35652         var cid = this.getColumnId(colIndex);
35653         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
35654         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
35655
35656         if(Roo.isSafari){
35657             this.updateHeaders();
35658         }
35659         this.updateSplitters();
35660         this.layout();
35661     },
35662
35663     insertRows : function(dm, firstRow, lastRow, isUpdate){
35664         if(firstRow == 0 && lastRow == dm.getCount()-1){
35665             this.refresh();
35666         }else{
35667             if(!isUpdate){
35668                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
35669             }
35670             var s = this.getScrollState();
35671             var markup = this.renderRows(firstRow, lastRow);
35672             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
35673             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
35674             this.restoreScroll(s);
35675             if(!isUpdate){
35676                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
35677                 this.syncRowHeights(firstRow, lastRow);
35678                 this.stripeRows(firstRow);
35679                 this.layout();
35680             }
35681         }
35682     },
35683
35684     bufferRows : function(markup, target, index){
35685         var before = null, trows = target.rows, tbody = target.tBodies[0];
35686         if(index < trows.length){
35687             before = trows[index];
35688         }
35689         var b = document.createElement("div");
35690         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
35691         var rows = b.firstChild.rows;
35692         for(var i = 0, len = rows.length; i < len; i++){
35693             if(before){
35694                 tbody.insertBefore(rows[0], before);
35695             }else{
35696                 tbody.appendChild(rows[0]);
35697             }
35698         }
35699         b.innerHTML = "";
35700         b = null;
35701     },
35702
35703     deleteRows : function(dm, firstRow, lastRow){
35704         if(dm.getRowCount()<1){
35705             this.fireEvent("beforerefresh", this);
35706             this.mainBody.update("");
35707             this.lockedBody.update("");
35708             this.fireEvent("refresh", this);
35709         }else{
35710             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
35711             var bt = this.getBodyTable();
35712             var tbody = bt.firstChild;
35713             var rows = bt.rows;
35714             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
35715                 tbody.removeChild(rows[firstRow]);
35716             }
35717             this.stripeRows(firstRow);
35718             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
35719         }
35720     },
35721
35722     updateRows : function(dataSource, firstRow, lastRow){
35723         var s = this.getScrollState();
35724         this.refresh();
35725         this.restoreScroll(s);
35726     },
35727
35728     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
35729         if(!noRefresh){
35730            this.refresh();
35731         }
35732         this.updateHeaderSortState();
35733     },
35734
35735     getScrollState : function(){
35736         
35737         var sb = this.scroller.dom;
35738         return {left: sb.scrollLeft, top: sb.scrollTop};
35739     },
35740
35741     stripeRows : function(startRow){
35742         if(!this.grid.stripeRows || this.ds.getCount() < 1){
35743             return;
35744         }
35745         startRow = startRow || 0;
35746         var rows = this.getBodyTable().rows;
35747         var lrows = this.getLockedTable().rows;
35748         var cls = ' x-grid-row-alt ';
35749         for(var i = startRow, len = rows.length; i < len; i++){
35750             var row = rows[i], lrow = lrows[i];
35751             var isAlt = ((i+1) % 2 == 0);
35752             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
35753             if(isAlt == hasAlt){
35754                 continue;
35755             }
35756             if(isAlt){
35757                 row.className += " x-grid-row-alt";
35758             }else{
35759                 row.className = row.className.replace("x-grid-row-alt", "");
35760             }
35761             if(lrow){
35762                 lrow.className = row.className;
35763             }
35764         }
35765     },
35766
35767     restoreScroll : function(state){
35768         //Roo.log('GridView.restoreScroll');
35769         var sb = this.scroller.dom;
35770         sb.scrollLeft = state.left;
35771         sb.scrollTop = state.top;
35772         this.syncScroll();
35773     },
35774
35775     syncScroll : function(){
35776         //Roo.log('GridView.syncScroll');
35777         var sb = this.scroller.dom;
35778         var sh = this.mainHd.dom;
35779         var bs = this.mainBody.dom;
35780         var lv = this.lockedBody.dom;
35781         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
35782         lv.scrollTop = bs.scrollTop = sb.scrollTop;
35783     },
35784
35785     handleScroll : function(e){
35786         this.syncScroll();
35787         var sb = this.scroller.dom;
35788         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
35789         e.stopEvent();
35790     },
35791
35792     handleWheel : function(e){
35793         var d = e.getWheelDelta();
35794         this.scroller.dom.scrollTop -= d*22;
35795         // set this here to prevent jumpy scrolling on large tables
35796         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
35797         e.stopEvent();
35798     },
35799
35800     renderRows : function(startRow, endRow){
35801         // pull in all the crap needed to render rows
35802         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
35803         var colCount = cm.getColumnCount();
35804
35805         if(ds.getCount() < 1){
35806             return ["", ""];
35807         }
35808
35809         // build a map for all the columns
35810         var cs = [];
35811         for(var i = 0; i < colCount; i++){
35812             var name = cm.getDataIndex(i);
35813             cs[i] = {
35814                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
35815                 renderer : cm.getRenderer(i),
35816                 id : cm.getColumnId(i),
35817                 locked : cm.isLocked(i)
35818             };
35819         }
35820
35821         startRow = startRow || 0;
35822         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
35823
35824         // records to render
35825         var rs = ds.getRange(startRow, endRow);
35826
35827         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
35828     },
35829
35830     // As much as I hate to duplicate code, this was branched because FireFox really hates
35831     // [].join("") on strings. The performance difference was substantial enough to
35832     // branch this function
35833     doRender : Roo.isGecko ?
35834             function(cs, rs, ds, startRow, colCount, stripe){
35835                 var ts = this.templates, ct = ts.cell, rt = ts.row;
35836                 // buffers
35837                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
35838                 
35839                 var hasListener = this.grid.hasListener('rowclass');
35840                 var rowcfg = {};
35841                 for(var j = 0, len = rs.length; j < len; j++){
35842                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
35843                     for(var i = 0; i < colCount; i++){
35844                         c = cs[i];
35845                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
35846                         p.id = c.id;
35847                         p.css = p.attr = "";
35848                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
35849                         if(p.value == undefined || p.value === "") p.value = "&#160;";
35850                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
35851                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
35852                         }
35853                         var markup = ct.apply(p);
35854                         if(!c.locked){
35855                             cb+= markup;
35856                         }else{
35857                             lcb+= markup;
35858                         }
35859                     }
35860                     var alt = [];
35861                     if(stripe && ((rowIndex+1) % 2 == 0)){
35862                         alt.push("x-grid-row-alt")
35863                     }
35864                     if(r.dirty){
35865                         alt.push(  " x-grid-dirty-row");
35866                     }
35867                     rp.cells = lcb;
35868                     if(this.getRowClass){
35869                         alt.push(this.getRowClass(r, rowIndex));
35870                     }
35871                     if (hasListener) {
35872                         rowcfg = {
35873                              
35874                             record: r,
35875                             rowIndex : rowIndex,
35876                             rowClass : ''
35877                         }
35878                         this.grid.fireEvent('rowclass', this, rowcfg);
35879                         alt.push(rowcfg.rowClass);
35880                     }
35881                     rp.alt = alt.join(" ");
35882                     lbuf+= rt.apply(rp);
35883                     rp.cells = cb;
35884                     buf+=  rt.apply(rp);
35885                 }
35886                 return [lbuf, buf];
35887             } :
35888             function(cs, rs, ds, startRow, colCount, stripe){
35889                 var ts = this.templates, ct = ts.cell, rt = ts.row;
35890                 // buffers
35891                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
35892                 var hasListener = this.grid.hasListener('rowclass');
35893  
35894                 var rowcfg = {};
35895                 for(var j = 0, len = rs.length; j < len; j++){
35896                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
35897                     for(var i = 0; i < colCount; i++){
35898                         c = cs[i];
35899                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
35900                         p.id = c.id;
35901                         p.css = p.attr = "";
35902                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
35903                         if(p.value == undefined || p.value === "") p.value = "&#160;";
35904                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
35905                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
35906                         }
35907                         
35908                         var markup = ct.apply(p);
35909                         if(!c.locked){
35910                             cb[cb.length] = markup;
35911                         }else{
35912                             lcb[lcb.length] = markup;
35913                         }
35914                     }
35915                     var alt = [];
35916                     if(stripe && ((rowIndex+1) % 2 == 0)){
35917                         alt.push( "x-grid-row-alt");
35918                     }
35919                     if(r.dirty){
35920                         alt.push(" x-grid-dirty-row");
35921                     }
35922                     rp.cells = lcb;
35923                     if(this.getRowClass){
35924                         alt.push( this.getRowClass(r, rowIndex));
35925                     }
35926                     if (hasListener) {
35927                         rowcfg = {
35928                              
35929                             record: r,
35930                             rowIndex : rowIndex,
35931                             rowClass : ''
35932                         }
35933                         this.grid.fireEvent('rowclass', this, rowcfg);
35934                         alt.push(rowcfg.rowClass);
35935                     }
35936                     rp.alt = alt.join(" ");
35937                     rp.cells = lcb.join("");
35938                     lbuf[lbuf.length] = rt.apply(rp);
35939                     rp.cells = cb.join("");
35940                     buf[buf.length] =  rt.apply(rp);
35941                 }
35942                 return [lbuf.join(""), buf.join("")];
35943             },
35944
35945     renderBody : function(){
35946         var markup = this.renderRows();
35947         var bt = this.templates.body;
35948         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
35949     },
35950
35951     /**
35952      * Refreshes the grid
35953      * @param {Boolean} headersToo
35954      */
35955     refresh : function(headersToo){
35956         this.fireEvent("beforerefresh", this);
35957         this.grid.stopEditing();
35958         var result = this.renderBody();
35959         this.lockedBody.update(result[0]);
35960         this.mainBody.update(result[1]);
35961         if(headersToo === true){
35962             this.updateHeaders();
35963             this.updateColumns();
35964             this.updateSplitters();
35965             this.updateHeaderSortState();
35966         }
35967         this.syncRowHeights();
35968         this.layout();
35969         this.fireEvent("refresh", this);
35970     },
35971
35972     handleColumnMove : function(cm, oldIndex, newIndex){
35973         this.indexMap = null;
35974         var s = this.getScrollState();
35975         this.refresh(true);
35976         this.restoreScroll(s);
35977         this.afterMove(newIndex);
35978     },
35979
35980     afterMove : function(colIndex){
35981         if(this.enableMoveAnim && Roo.enableFx){
35982             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
35983         }
35984         // if multisort - fix sortOrder, and reload..
35985         if (this.grid.dataSource.multiSort) {
35986             // the we can call sort again..
35987             var dm = this.grid.dataSource;
35988             var cm = this.grid.colModel;
35989             var so = [];
35990             for(var i = 0; i < cm.config.length; i++ ) {
35991                 
35992                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
35993                     continue; // dont' bother, it's not in sort list or being set.
35994                 }
35995                 
35996                 so.push(cm.config[i].dataIndex);
35997             };
35998             dm.sortOrder = so;
35999             dm.load(dm.lastOptions);
36000             
36001             
36002         }
36003         
36004     },
36005
36006     updateCell : function(dm, rowIndex, dataIndex){
36007         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
36008         if(typeof colIndex == "undefined"){ // not present in grid
36009             return;
36010         }
36011         var cm = this.grid.colModel;
36012         var cell = this.getCell(rowIndex, colIndex);
36013         var cellText = this.getCellText(rowIndex, colIndex);
36014
36015         var p = {
36016             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
36017             id : cm.getColumnId(colIndex),
36018             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
36019         };
36020         var renderer = cm.getRenderer(colIndex);
36021         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
36022         if(typeof val == "undefined" || val === "") val = "&#160;";
36023         cellText.innerHTML = val;
36024         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
36025         this.syncRowHeights(rowIndex, rowIndex);
36026     },
36027
36028     calcColumnWidth : function(colIndex, maxRowsToMeasure){
36029         var maxWidth = 0;
36030         if(this.grid.autoSizeHeaders){
36031             var h = this.getHeaderCellMeasure(colIndex);
36032             maxWidth = Math.max(maxWidth, h.scrollWidth);
36033         }
36034         var tb, index;
36035         if(this.cm.isLocked(colIndex)){
36036             tb = this.getLockedTable();
36037             index = colIndex;
36038         }else{
36039             tb = this.getBodyTable();
36040             index = colIndex - this.cm.getLockedCount();
36041         }
36042         if(tb && tb.rows){
36043             var rows = tb.rows;
36044             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
36045             for(var i = 0; i < stopIndex; i++){
36046                 var cell = rows[i].childNodes[index].firstChild;
36047                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
36048             }
36049         }
36050         return maxWidth + /*margin for error in IE*/ 5;
36051     },
36052     /**
36053      * Autofit a column to its content.
36054      * @param {Number} colIndex
36055      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
36056      */
36057      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
36058          if(this.cm.isHidden(colIndex)){
36059              return; // can't calc a hidden column
36060          }
36061         if(forceMinSize){
36062             var cid = this.cm.getColumnId(colIndex);
36063             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
36064            if(this.grid.autoSizeHeaders){
36065                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
36066            }
36067         }
36068         var newWidth = this.calcColumnWidth(colIndex);
36069         this.cm.setColumnWidth(colIndex,
36070             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
36071         if(!suppressEvent){
36072             this.grid.fireEvent("columnresize", colIndex, newWidth);
36073         }
36074     },
36075
36076     /**
36077      * Autofits all columns to their content and then expands to fit any extra space in the grid
36078      */
36079      autoSizeColumns : function(){
36080         var cm = this.grid.colModel;
36081         var colCount = cm.getColumnCount();
36082         for(var i = 0; i < colCount; i++){
36083             this.autoSizeColumn(i, true, true);
36084         }
36085         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
36086             this.fitColumns();
36087         }else{
36088             this.updateColumns();
36089             this.layout();
36090         }
36091     },
36092
36093     /**
36094      * Autofits all columns to the grid's width proportionate with their current size
36095      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
36096      */
36097     fitColumns : function(reserveScrollSpace){
36098         var cm = this.grid.colModel;
36099         var colCount = cm.getColumnCount();
36100         var cols = [];
36101         var width = 0;
36102         var i, w;
36103         for (i = 0; i < colCount; i++){
36104             if(!cm.isHidden(i) && !cm.isFixed(i)){
36105                 w = cm.getColumnWidth(i);
36106                 cols.push(i);
36107                 cols.push(w);
36108                 width += w;
36109             }
36110         }
36111         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
36112         if(reserveScrollSpace){
36113             avail -= 17;
36114         }
36115         var frac = (avail - cm.getTotalWidth())/width;
36116         while (cols.length){
36117             w = cols.pop();
36118             i = cols.pop();
36119             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
36120         }
36121         this.updateColumns();
36122         this.layout();
36123     },
36124
36125     onRowSelect : function(rowIndex){
36126         var row = this.getRowComposite(rowIndex);
36127         row.addClass("x-grid-row-selected");
36128     },
36129
36130     onRowDeselect : function(rowIndex){
36131         var row = this.getRowComposite(rowIndex);
36132         row.removeClass("x-grid-row-selected");
36133     },
36134
36135     onCellSelect : function(row, col){
36136         var cell = this.getCell(row, col);
36137         if(cell){
36138             Roo.fly(cell).addClass("x-grid-cell-selected");
36139         }
36140     },
36141
36142     onCellDeselect : function(row, col){
36143         var cell = this.getCell(row, col);
36144         if(cell){
36145             Roo.fly(cell).removeClass("x-grid-cell-selected");
36146         }
36147     },
36148
36149     updateHeaderSortState : function(){
36150         
36151         // sort state can be single { field: xxx, direction : yyy}
36152         // or   { xxx=>ASC , yyy : DESC ..... }
36153         
36154         var mstate = {};
36155         if (!this.ds.multiSort) { 
36156             var state = this.ds.getSortState();
36157             if(!state){
36158                 return;
36159             }
36160             mstate[state.field] = state.direction;
36161             // FIXME... - this is not used here.. but might be elsewhere..
36162             this.sortState = state;
36163             
36164         } else {
36165             mstate = this.ds.sortToggle;
36166         }
36167         //remove existing sort classes..
36168         
36169         var sc = this.sortClasses;
36170         var hds = this.el.select(this.headerSelector).removeClass(sc);
36171         
36172         for(var f in mstate) {
36173         
36174             var sortColumn = this.cm.findColumnIndex(f);
36175             
36176             if(sortColumn != -1){
36177                 var sortDir = mstate[f];        
36178                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
36179             }
36180         }
36181         
36182          
36183         
36184     },
36185
36186
36187     handleHeaderClick : function(g, index){
36188         if(this.headersDisabled){
36189             return;
36190         }
36191         var dm = g.dataSource, cm = g.colModel;
36192         if(!cm.isSortable(index)){
36193             return;
36194         }
36195         g.stopEditing();
36196         
36197         if (dm.multiSort) {
36198             // update the sortOrder
36199             var so = [];
36200             for(var i = 0; i < cm.config.length; i++ ) {
36201                 
36202                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
36203                     continue; // dont' bother, it's not in sort list or being set.
36204                 }
36205                 
36206                 so.push(cm.config[i].dataIndex);
36207             };
36208             dm.sortOrder = so;
36209         }
36210         
36211         
36212         dm.sort(cm.getDataIndex(index));
36213     },
36214
36215
36216     destroy : function(){
36217         if(this.colMenu){
36218             this.colMenu.removeAll();
36219             Roo.menu.MenuMgr.unregister(this.colMenu);
36220             this.colMenu.getEl().remove();
36221             delete this.colMenu;
36222         }
36223         if(this.hmenu){
36224             this.hmenu.removeAll();
36225             Roo.menu.MenuMgr.unregister(this.hmenu);
36226             this.hmenu.getEl().remove();
36227             delete this.hmenu;
36228         }
36229         if(this.grid.enableColumnMove){
36230             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
36231             if(dds){
36232                 for(var dd in dds){
36233                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
36234                         var elid = dds[dd].dragElId;
36235                         dds[dd].unreg();
36236                         Roo.get(elid).remove();
36237                     } else if(dds[dd].config.isTarget){
36238                         dds[dd].proxyTop.remove();
36239                         dds[dd].proxyBottom.remove();
36240                         dds[dd].unreg();
36241                     }
36242                     if(Roo.dd.DDM.locationCache[dd]){
36243                         delete Roo.dd.DDM.locationCache[dd];
36244                     }
36245                 }
36246                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
36247             }
36248         }
36249         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
36250         this.bind(null, null);
36251         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
36252     },
36253
36254     handleLockChange : function(){
36255         this.refresh(true);
36256     },
36257
36258     onDenyColumnLock : function(){
36259
36260     },
36261
36262     onDenyColumnHide : function(){
36263
36264     },
36265
36266     handleHdMenuClick : function(item){
36267         var index = this.hdCtxIndex;
36268         var cm = this.cm, ds = this.ds;
36269         switch(item.id){
36270             case "asc":
36271                 ds.sort(cm.getDataIndex(index), "ASC");
36272                 break;
36273             case "desc":
36274                 ds.sort(cm.getDataIndex(index), "DESC");
36275                 break;
36276             case "lock":
36277                 var lc = cm.getLockedCount();
36278                 if(cm.getColumnCount(true) <= lc+1){
36279                     this.onDenyColumnLock();
36280                     return;
36281                 }
36282                 if(lc != index){
36283                     cm.setLocked(index, true, true);
36284                     cm.moveColumn(index, lc);
36285                     this.grid.fireEvent("columnmove", index, lc);
36286                 }else{
36287                     cm.setLocked(index, true);
36288                 }
36289             break;
36290             case "unlock":
36291                 var lc = cm.getLockedCount();
36292                 if((lc-1) != index){
36293                     cm.setLocked(index, false, true);
36294                     cm.moveColumn(index, lc-1);
36295                     this.grid.fireEvent("columnmove", index, lc-1);
36296                 }else{
36297                     cm.setLocked(index, false);
36298                 }
36299             break;
36300             default:
36301                 index = cm.getIndexById(item.id.substr(4));
36302                 if(index != -1){
36303                     if(item.checked && cm.getColumnCount(true) <= 1){
36304                         this.onDenyColumnHide();
36305                         return false;
36306                     }
36307                     cm.setHidden(index, item.checked);
36308                 }
36309         }
36310         return true;
36311     },
36312
36313     beforeColMenuShow : function(){
36314         var cm = this.cm,  colCount = cm.getColumnCount();
36315         this.colMenu.removeAll();
36316         for(var i = 0; i < colCount; i++){
36317             this.colMenu.add(new Roo.menu.CheckItem({
36318                 id: "col-"+cm.getColumnId(i),
36319                 text: cm.getColumnHeader(i),
36320                 checked: !cm.isHidden(i),
36321                 hideOnClick:false
36322             }));
36323         }
36324     },
36325
36326     handleHdCtx : function(g, index, e){
36327         e.stopEvent();
36328         var hd = this.getHeaderCell(index);
36329         this.hdCtxIndex = index;
36330         var ms = this.hmenu.items, cm = this.cm;
36331         ms.get("asc").setDisabled(!cm.isSortable(index));
36332         ms.get("desc").setDisabled(!cm.isSortable(index));
36333         if(this.grid.enableColLock !== false){
36334             ms.get("lock").setDisabled(cm.isLocked(index));
36335             ms.get("unlock").setDisabled(!cm.isLocked(index));
36336         }
36337         this.hmenu.show(hd, "tl-bl");
36338     },
36339
36340     handleHdOver : function(e){
36341         var hd = this.findHeaderCell(e.getTarget());
36342         if(hd && !this.headersDisabled){
36343             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
36344                this.fly(hd).addClass("x-grid-hd-over");
36345             }
36346         }
36347     },
36348
36349     handleHdOut : function(e){
36350         var hd = this.findHeaderCell(e.getTarget());
36351         if(hd){
36352             this.fly(hd).removeClass("x-grid-hd-over");
36353         }
36354     },
36355
36356     handleSplitDblClick : function(e, t){
36357         var i = this.getCellIndex(t);
36358         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
36359             this.autoSizeColumn(i, true);
36360             this.layout();
36361         }
36362     },
36363
36364     render : function(){
36365
36366         var cm = this.cm;
36367         var colCount = cm.getColumnCount();
36368
36369         if(this.grid.monitorWindowResize === true){
36370             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
36371         }
36372         var header = this.renderHeaders();
36373         var body = this.templates.body.apply({rows:""});
36374         var html = this.templates.master.apply({
36375             lockedBody: body,
36376             body: body,
36377             lockedHeader: header[0],
36378             header: header[1]
36379         });
36380
36381         //this.updateColumns();
36382
36383         this.grid.getGridEl().dom.innerHTML = html;
36384
36385         this.initElements();
36386         
36387         // a kludge to fix the random scolling effect in webkit
36388         this.el.on("scroll", function() {
36389             this.el.dom.scrollTop=0; // hopefully not recursive..
36390         },this);
36391
36392         this.scroller.on("scroll", this.handleScroll, this);
36393         this.lockedBody.on("mousewheel", this.handleWheel, this);
36394         this.mainBody.on("mousewheel", this.handleWheel, this);
36395
36396         this.mainHd.on("mouseover", this.handleHdOver, this);
36397         this.mainHd.on("mouseout", this.handleHdOut, this);
36398         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
36399                 {delegate: "."+this.splitClass});
36400
36401         this.lockedHd.on("mouseover", this.handleHdOver, this);
36402         this.lockedHd.on("mouseout", this.handleHdOut, this);
36403         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
36404                 {delegate: "."+this.splitClass});
36405
36406         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
36407             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
36408         }
36409
36410         this.updateSplitters();
36411
36412         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
36413             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
36414             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
36415         }
36416
36417         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
36418             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
36419             this.hmenu.add(
36420                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
36421                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
36422             );
36423             if(this.grid.enableColLock !== false){
36424                 this.hmenu.add('-',
36425                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
36426                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
36427                 );
36428             }
36429             if(this.grid.enableColumnHide !== false){
36430
36431                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
36432                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
36433                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
36434
36435                 this.hmenu.add('-',
36436                     {id:"columns", text: this.columnsText, menu: this.colMenu}
36437                 );
36438             }
36439             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
36440
36441             this.grid.on("headercontextmenu", this.handleHdCtx, this);
36442         }
36443
36444         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
36445             this.dd = new Roo.grid.GridDragZone(this.grid, {
36446                 ddGroup : this.grid.ddGroup || 'GridDD'
36447             });
36448         }
36449
36450         /*
36451         for(var i = 0; i < colCount; i++){
36452             if(cm.isHidden(i)){
36453                 this.hideColumn(i);
36454             }
36455             if(cm.config[i].align){
36456                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
36457                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
36458             }
36459         }*/
36460         
36461         this.updateHeaderSortState();
36462
36463         this.beforeInitialResize();
36464         this.layout(true);
36465
36466         // two part rendering gives faster view to the user
36467         this.renderPhase2.defer(1, this);
36468     },
36469
36470     renderPhase2 : function(){
36471         // render the rows now
36472         this.refresh();
36473         if(this.grid.autoSizeColumns){
36474             this.autoSizeColumns();
36475         }
36476     },
36477
36478     beforeInitialResize : function(){
36479
36480     },
36481
36482     onColumnSplitterMoved : function(i, w){
36483         this.userResized = true;
36484         var cm = this.grid.colModel;
36485         cm.setColumnWidth(i, w, true);
36486         var cid = cm.getColumnId(i);
36487         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
36488         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
36489         this.updateSplitters();
36490         this.layout();
36491         this.grid.fireEvent("columnresize", i, w);
36492     },
36493
36494     syncRowHeights : function(startIndex, endIndex){
36495         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
36496             startIndex = startIndex || 0;
36497             var mrows = this.getBodyTable().rows;
36498             var lrows = this.getLockedTable().rows;
36499             var len = mrows.length-1;
36500             endIndex = Math.min(endIndex || len, len);
36501             for(var i = startIndex; i <= endIndex; i++){
36502                 var m = mrows[i], l = lrows[i];
36503                 var h = Math.max(m.offsetHeight, l.offsetHeight);
36504                 m.style.height = l.style.height = h + "px";
36505             }
36506         }
36507     },
36508
36509     layout : function(initialRender, is2ndPass){
36510         var g = this.grid;
36511         var auto = g.autoHeight;
36512         var scrollOffset = 16;
36513         var c = g.getGridEl(), cm = this.cm,
36514                 expandCol = g.autoExpandColumn,
36515                 gv = this;
36516         //c.beginMeasure();
36517
36518         if(!c.dom.offsetWidth){ // display:none?
36519             if(initialRender){
36520                 this.lockedWrap.show();
36521                 this.mainWrap.show();
36522             }
36523             return;
36524         }
36525
36526         var hasLock = this.cm.isLocked(0);
36527
36528         var tbh = this.headerPanel.getHeight();
36529         var bbh = this.footerPanel.getHeight();
36530
36531         if(auto){
36532             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
36533             var newHeight = ch + c.getBorderWidth("tb");
36534             if(g.maxHeight){
36535                 newHeight = Math.min(g.maxHeight, newHeight);
36536             }
36537             c.setHeight(newHeight);
36538         }
36539
36540         if(g.autoWidth){
36541             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
36542         }
36543
36544         var s = this.scroller;
36545
36546         var csize = c.getSize(true);
36547
36548         this.el.setSize(csize.width, csize.height);
36549
36550         this.headerPanel.setWidth(csize.width);
36551         this.footerPanel.setWidth(csize.width);
36552
36553         var hdHeight = this.mainHd.getHeight();
36554         var vw = csize.width;
36555         var vh = csize.height - (tbh + bbh);
36556
36557         s.setSize(vw, vh);
36558
36559         var bt = this.getBodyTable();
36560         var ltWidth = hasLock ?
36561                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
36562
36563         var scrollHeight = bt.offsetHeight;
36564         var scrollWidth = ltWidth + bt.offsetWidth;
36565         var vscroll = false, hscroll = false;
36566
36567         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
36568
36569         var lw = this.lockedWrap, mw = this.mainWrap;
36570         var lb = this.lockedBody, mb = this.mainBody;
36571
36572         setTimeout(function(){
36573             var t = s.dom.offsetTop;
36574             var w = s.dom.clientWidth,
36575                 h = s.dom.clientHeight;
36576
36577             lw.setTop(t);
36578             lw.setSize(ltWidth, h);
36579
36580             mw.setLeftTop(ltWidth, t);
36581             mw.setSize(w-ltWidth, h);
36582
36583             lb.setHeight(h-hdHeight);
36584             mb.setHeight(h-hdHeight);
36585
36586             if(is2ndPass !== true && !gv.userResized && expandCol){
36587                 // high speed resize without full column calculation
36588                 
36589                 var ci = cm.getIndexById(expandCol);
36590                 if (ci < 0) {
36591                     ci = cm.findColumnIndex(expandCol);
36592                 }
36593                 ci = Math.max(0, ci); // make sure it's got at least the first col.
36594                 var expandId = cm.getColumnId(ci);
36595                 var  tw = cm.getTotalWidth(false);
36596                 var currentWidth = cm.getColumnWidth(ci);
36597                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
36598                 if(currentWidth != cw){
36599                     cm.setColumnWidth(ci, cw, true);
36600                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
36601                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
36602                     gv.updateSplitters();
36603                     gv.layout(false, true);
36604                 }
36605             }
36606
36607             if(initialRender){
36608                 lw.show();
36609                 mw.show();
36610             }
36611             //c.endMeasure();
36612         }, 10);
36613     },
36614
36615     onWindowResize : function(){
36616         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
36617             return;
36618         }
36619         this.layout();
36620     },
36621
36622     appendFooter : function(parentEl){
36623         return null;
36624     },
36625
36626     sortAscText : "Sort Ascending",
36627     sortDescText : "Sort Descending",
36628     lockText : "Lock Column",
36629     unlockText : "Unlock Column",
36630     columnsText : "Columns"
36631 });
36632
36633
36634 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
36635     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
36636     this.proxy.el.addClass('x-grid3-col-dd');
36637 };
36638
36639 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
36640     handleMouseDown : function(e){
36641
36642     },
36643
36644     callHandleMouseDown : function(e){
36645         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
36646     }
36647 });