roojs-ui-debug.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13
14 /*
15  * These classes are derivatives of the similarly named classes in the YUI Library.
16  * The original license:
17  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18  * Code licensed under the BSD License:
19  * http://developer.yahoo.net/yui/license.txt
20  */
21
22 (function() {
23
24 var Event=Roo.EventManager;
25 var Dom=Roo.lib.Dom;
26
27 /**
28  * @class Roo.dd.DragDrop
29  * @extends Roo.util.Observable
30  * Defines the interface and base operation of items that that can be
31  * dragged or can be drop targets.  It was designed to be extended, overriding
32  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
33  * Up to three html elements can be associated with a DragDrop instance:
34  * <ul>
35  * <li>linked element: the element that is passed into the constructor.
36  * This is the element which defines the boundaries for interaction with
37  * other DragDrop objects.</li>
38  * <li>handle element(s): The drag operation only occurs if the element that
39  * was clicked matches a handle element.  By default this is the linked
40  * element, but there are times that you will want only a portion of the
41  * linked element to initiate the drag operation, and the setHandleElId()
42  * method provides a way to define this.</li>
43  * <li>drag element: this represents the element that would be moved along
44  * with the cursor during a drag operation.  By default, this is the linked
45  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
46  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
47  * </li>
48  * </ul>
49  * This class should not be instantiated until the onload event to ensure that
50  * the associated elements are available.
51  * The following would define a DragDrop obj that would interact with any
52  * other DragDrop obj in the "group1" group:
53  * <pre>
54  *  dd = new Roo.dd.DragDrop("div1", "group1");
55  * </pre>
56  * Since none of the event handlers have been implemented, nothing would
57  * actually happen if you were to run the code above.  Normally you would
58  * override this class or one of the default implementations, but you can
59  * also override the methods you want on an instance of the class...
60  * <pre>
61  *  dd.onDragDrop = function(e, id) {
62  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
63  *  }
64  * </pre>
65  * @constructor
66  * @param {String} id of the element that is linked to this instance
67  * @param {String} sGroup the group of related DragDrop objects
68  * @param {object} config an object containing configurable attributes
69  *                Valid properties for DragDrop:
70  *                    padding, isTarget, maintainOffset, primaryButtonOnly
71  */
72 Roo.dd.DragDrop = function(id, sGroup, config) {
73     if (id) {
74         this.init(id, sGroup, config);
75     }
76     
77 };
78
79 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
80
81     /**
82      * The id of the element associated with this object.  This is what we
83      * refer to as the "linked element" because the size and position of
84      * this element is used to determine when the drag and drop objects have
85      * interacted.
86      * @property id
87      * @type String
88      */
89     id: null,
90
91     /**
92      * Configuration attributes passed into the constructor
93      * @property config
94      * @type object
95      */
96     config: null,
97
98     /**
99      * The id of the element that will be dragged.  By default this is same
100      * as the linked element , but could be changed to another element. Ex:
101      * Roo.dd.DDProxy
102      * @property dragElId
103      * @type String
104      * @private
105      */
106     dragElId: null,
107
108     /**
109      * the id of the element that initiates the drag operation.  By default
110      * this is the linked element, but could be changed to be a child of this
111      * element.  This lets us do things like only starting the drag when the
112      * header element within the linked html element is clicked.
113      * @property handleElId
114      * @type String
115      * @private
116      */
117     handleElId: null,
118
119     /**
120      * An associative array of HTML tags that will be ignored if clicked.
121      * @property invalidHandleTypes
122      * @type {string: string}
123      */
124     invalidHandleTypes: null,
125
126     /**
127      * An associative array of ids for elements that will be ignored if clicked
128      * @property invalidHandleIds
129      * @type {string: string}
130      */
131     invalidHandleIds: null,
132
133     /**
134      * An indexted array of css class names for elements that will be ignored
135      * if clicked.
136      * @property invalidHandleClasses
137      * @type string[]
138      */
139     invalidHandleClasses: null,
140
141     /**
142      * The linked element's absolute X position at the time the drag was
143      * started
144      * @property startPageX
145      * @type int
146      * @private
147      */
148     startPageX: 0,
149
150     /**
151      * The linked element's absolute X position at the time the drag was
152      * started
153      * @property startPageY
154      * @type int
155      * @private
156      */
157     startPageY: 0,
158
159     /**
160      * The group defines a logical collection of DragDrop objects that are
161      * related.  Instances only get events when interacting with other
162      * DragDrop object in the same group.  This lets us define multiple
163      * groups using a single DragDrop subclass if we want.
164      * @property groups
165      * @type {string: string}
166      */
167     groups: null,
168
169     /**
170      * Individual drag/drop instances can be locked.  This will prevent
171      * onmousedown start drag.
172      * @property locked
173      * @type boolean
174      * @private
175      */
176     locked: false,
177
178     /**
179      * Lock this instance
180      * @method lock
181      */
182     lock: function() { this.locked = true; },
183
184     /**
185      * Unlock this instace
186      * @method unlock
187      */
188     unlock: function() { this.locked = false; },
189
190     /**
191      * By default, all insances can be a drop target.  This can be disabled by
192      * setting isTarget to false.
193      * @method isTarget
194      * @type boolean
195      */
196     isTarget: true,
197
198     /**
199      * The padding configured for this drag and drop object for calculating
200      * the drop zone intersection with this object.
201      * @method padding
202      * @type int[]
203      */
204     padding: null,
205
206     /**
207      * Cached reference to the linked element
208      * @property _domRef
209      * @private
210      */
211     _domRef: null,
212
213     /**
214      * Internal typeof flag
215      * @property __ygDragDrop
216      * @private
217      */
218     __ygDragDrop: true,
219
220     /**
221      * Set to true when horizontal contraints are applied
222      * @property constrainX
223      * @type boolean
224      * @private
225      */
226     constrainX: false,
227
228     /**
229      * Set to true when vertical contraints are applied
230      * @property constrainY
231      * @type boolean
232      * @private
233      */
234     constrainY: false,
235
236     /**
237      * The left constraint
238      * @property minX
239      * @type int
240      * @private
241      */
242     minX: 0,
243
244     /**
245      * The right constraint
246      * @property maxX
247      * @type int
248      * @private
249      */
250     maxX: 0,
251
252     /**
253      * The up constraint
254      * @property minY
255      * @type int
256      * @type int
257      * @private
258      */
259     minY: 0,
260
261     /**
262      * The down constraint
263      * @property maxY
264      * @type int
265      * @private
266      */
267     maxY: 0,
268
269     /**
270      * Maintain offsets when we resetconstraints.  Set to true when you want
271      * the position of the element relative to its parent to stay the same
272      * when the page changes
273      *
274      * @property maintainOffset
275      * @type boolean
276      */
277     maintainOffset: false,
278
279     /**
280      * Array of pixel locations the element will snap to if we specified a
281      * horizontal graduation/interval.  This array is generated automatically
282      * when you define a tick interval.
283      * @property xTicks
284      * @type int[]
285      */
286     xTicks: null,
287
288     /**
289      * Array of pixel locations the element will snap to if we specified a
290      * vertical graduation/interval.  This array is generated automatically
291      * when you define a tick interval.
292      * @property yTicks
293      * @type int[]
294      */
295     yTicks: null,
296
297     /**
298      * By default the drag and drop instance will only respond to the primary
299      * button click (left button for a right-handed mouse).  Set to true to
300      * allow drag and drop to start with any mouse click that is propogated
301      * by the browser
302      * @property primaryButtonOnly
303      * @type boolean
304      */
305     primaryButtonOnly: true,
306
307     /**
308      * The availabe property is false until the linked dom element is accessible.
309      * @property available
310      * @type boolean
311      */
312     available: false,
313
314     /**
315      * By default, drags can only be initiated if the mousedown occurs in the
316      * region the linked element is.  This is done in part to work around a
317      * bug in some browsers that mis-report the mousedown if the previous
318      * mouseup happened outside of the window.  This property is set to true
319      * if outer handles are defined.
320      *
321      * @property hasOuterHandles
322      * @type boolean
323      * @default false
324      */
325     hasOuterHandles: false,
326
327     /**
328      * Code that executes immediately before the startDrag event
329      * @method b4StartDrag
330      * @private
331      */
332     b4StartDrag: function(x, y) { },
333
334     /**
335      * Abstract method called after a drag/drop object is clicked
336      * and the drag or mousedown time thresholds have beeen met.
337      * @method startDrag
338      * @param {int} X click location
339      * @param {int} Y click location
340      */
341     startDrag: function(x, y) { /* override this */ },
342
343     /**
344      * Code that executes immediately before the onDrag event
345      * @method b4Drag
346      * @private
347      */
348     b4Drag: function(e) { },
349
350     /**
351      * Abstract method called during the onMouseMove event while dragging an
352      * object.
353      * @method onDrag
354      * @param {Event} e the mousemove event
355      */
356     onDrag: function(e) { /* override this */ },
357
358     /**
359      * Abstract method called when this element fist begins hovering over
360      * another DragDrop obj
361      * @method onDragEnter
362      * @param {Event} e the mousemove event
363      * @param {String|DragDrop[]} id In POINT mode, the element
364      * id this is hovering over.  In INTERSECT mode, an array of one or more
365      * dragdrop items being hovered over.
366      */
367     onDragEnter: function(e, id) { /* override this */ },
368
369     /**
370      * Code that executes immediately before the onDragOver event
371      * @method b4DragOver
372      * @private
373      */
374     b4DragOver: function(e) { },
375
376     /**
377      * Abstract method called when this element is hovering over another
378      * DragDrop obj
379      * @method onDragOver
380      * @param {Event} e the mousemove event
381      * @param {String|DragDrop[]} id In POINT mode, the element
382      * id this is hovering over.  In INTERSECT mode, an array of dd items
383      * being hovered over.
384      */
385     onDragOver: function(e, id) { /* override this */ },
386
387     /**
388      * Code that executes immediately before the onDragOut event
389      * @method b4DragOut
390      * @private
391      */
392     b4DragOut: function(e) { },
393
394     /**
395      * Abstract method called when we are no longer hovering over an element
396      * @method onDragOut
397      * @param {Event} e the mousemove event
398      * @param {String|DragDrop[]} id In POINT mode, the element
399      * id this was hovering over.  In INTERSECT mode, an array of dd items
400      * that the mouse is no longer over.
401      */
402     onDragOut: function(e, id) { /* override this */ },
403
404     /**
405      * Code that executes immediately before the onDragDrop event
406      * @method b4DragDrop
407      * @private
408      */
409     b4DragDrop: function(e) { },
410
411     /**
412      * Abstract method called when this item is dropped on another DragDrop
413      * obj
414      * @method onDragDrop
415      * @param {Event} e the mouseup event
416      * @param {String|DragDrop[]} id In POINT mode, the element
417      * id this was dropped on.  In INTERSECT mode, an array of dd items this
418      * was dropped on.
419      */
420     onDragDrop: function(e, id) { /* override this */ },
421
422     /**
423      * Abstract method called when this item is dropped on an area with no
424      * drop target
425      * @method onInvalidDrop
426      * @param {Event} e the mouseup event
427      */
428     onInvalidDrop: function(e) { /* override this */ },
429
430     /**
431      * Code that executes immediately before the endDrag event
432      * @method b4EndDrag
433      * @private
434      */
435     b4EndDrag: function(e) { },
436
437     /**
438      * Fired when we are done dragging the object
439      * @method endDrag
440      * @param {Event} e the mouseup event
441      */
442     endDrag: function(e) { /* override this */ },
443
444     /**
445      * Code executed immediately before the onMouseDown event
446      * @method b4MouseDown
447      * @param {Event} e the mousedown event
448      * @private
449      */
450     b4MouseDown: function(e) {  },
451
452     /**
453      * Event handler that fires when a drag/drop obj gets a mousedown
454      * @method onMouseDown
455      * @param {Event} e the mousedown event
456      */
457     onMouseDown: function(e) { /* override this */ },
458
459     /**
460      * Event handler that fires when a drag/drop obj gets a mouseup
461      * @method onMouseUp
462      * @param {Event} e the mouseup event
463      */
464     onMouseUp: function(e) { /* override this */ },
465
466     /**
467      * Override the onAvailable method to do what is needed after the initial
468      * position was determined.
469      * @method onAvailable
470      */
471     onAvailable: function () {
472     },
473
474     /*
475      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
476      * @type Object
477      */
478     defaultPadding : {left:0, right:0, top:0, bottom:0},
479
480     /*
481      * Initializes the drag drop object's constraints to restrict movement to a certain element.
482  *
483  * Usage:
484  <pre><code>
485  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
486                 { dragElId: "existingProxyDiv" });
487  dd.startDrag = function(){
488      this.constrainTo("parent-id");
489  };
490  </code></pre>
491  * Or you can initalize it using the {@link Roo.Element} object:
492  <pre><code>
493  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
494      startDrag : function(){
495          this.constrainTo("parent-id");
496      }
497  });
498  </code></pre>
499      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
500      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
501      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
502      * an object containing the sides to pad. For example: {right:10, bottom:10}
503      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
504      */
505     constrainTo : function(constrainTo, pad, inContent){
506         if(typeof pad == "number"){
507             pad = {left: pad, right:pad, top:pad, bottom:pad};
508         }
509         pad = pad || this.defaultPadding;
510         var b = Roo.get(this.getEl()).getBox();
511         var ce = Roo.get(constrainTo);
512         var s = ce.getScroll();
513         var c, cd = ce.dom;
514         if(cd == document.body){
515             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
516         }else{
517             xy = ce.getXY();
518             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
519         }
520
521
522         var topSpace = b.y - c.y;
523         var leftSpace = b.x - c.x;
524
525         this.resetConstraints();
526         this.setXConstraint(leftSpace - (pad.left||0), // left
527                 c.width - leftSpace - b.width - (pad.right||0) //right
528         );
529         this.setYConstraint(topSpace - (pad.top||0), //top
530                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
531         );
532     },
533
534     /**
535      * Returns a reference to the linked element
536      * @method getEl
537      * @return {HTMLElement} the html element
538      */
539     getEl: function() {
540         if (!this._domRef) {
541             this._domRef = Roo.getDom(this.id);
542         }
543
544         return this._domRef;
545     },
546
547     /**
548      * Returns a reference to the actual element to drag.  By default this is
549      * the same as the html element, but it can be assigned to another
550      * element. An example of this can be found in Roo.dd.DDProxy
551      * @method getDragEl
552      * @return {HTMLElement} the html element
553      */
554     getDragEl: function() {
555         return Roo.getDom(this.dragElId);
556     },
557
558     /**
559      * Sets up the DragDrop object.  Must be called in the constructor of any
560      * Roo.dd.DragDrop subclass
561      * @method init
562      * @param id the id of the linked element
563      * @param {String} sGroup the group of related items
564      * @param {object} config configuration attributes
565      */
566     init: function(id, sGroup, config) {
567         this.initTarget(id, sGroup, config);
568         Event.on(this.id, "mousedown", this.handleMouseDown, this);
569         // Event.on(this.id, "selectstart", Event.preventDefault);
570     },
571
572     /**
573      * Initializes Targeting functionality only... the object does not
574      * get a mousedown handler.
575      * @method initTarget
576      * @param id the id of the linked element
577      * @param {String} sGroup the group of related items
578      * @param {object} config configuration attributes
579      */
580     initTarget: function(id, sGroup, config) {
581
582         // configuration attributes
583         this.config = config || {};
584
585         // create a local reference to the drag and drop manager
586         this.DDM = Roo.dd.DDM;
587         // initialize the groups array
588         this.groups = {};
589
590         // assume that we have an element reference instead of an id if the
591         // parameter is not a string
592         if (typeof id !== "string") {
593             id = Roo.id(id);
594         }
595
596         // set the id
597         this.id = id;
598
599         // add to an interaction group
600         this.addToGroup((sGroup) ? sGroup : "default");
601
602         // We don't want to register this as the handle with the manager
603         // so we just set the id rather than calling the setter.
604         this.handleElId = id;
605
606         // the linked element is the element that gets dragged by default
607         this.setDragElId(id);
608
609         // by default, clicked anchors will not start drag operations.
610         this.invalidHandleTypes = { A: "A" };
611         this.invalidHandleIds = {};
612         this.invalidHandleClasses = [];
613
614         this.applyConfig();
615
616         this.handleOnAvailable();
617     },
618
619     /**
620      * Applies the configuration parameters that were passed into the constructor.
621      * This is supposed to happen at each level through the inheritance chain.  So
622      * a DDProxy implentation will execute apply config on DDProxy, DD, and
623      * DragDrop in order to get all of the parameters that are available in
624      * each object.
625      * @method applyConfig
626      */
627     applyConfig: function() {
628
629         // configurable properties:
630         //    padding, isTarget, maintainOffset, primaryButtonOnly
631         this.padding           = this.config.padding || [0, 0, 0, 0];
632         this.isTarget          = (this.config.isTarget !== false);
633         this.maintainOffset    = (this.config.maintainOffset);
634         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
635
636     },
637
638     /**
639      * Executed when the linked element is available
640      * @method handleOnAvailable
641      * @private
642      */
643     handleOnAvailable: function() {
644         this.available = true;
645         this.resetConstraints();
646         this.onAvailable();
647     },
648
649      /**
650      * Configures the padding for the target zone in px.  Effectively expands
651      * (or reduces) the virtual object size for targeting calculations.
652      * Supports css-style shorthand; if only one parameter is passed, all sides
653      * will have that padding, and if only two are passed, the top and bottom
654      * will have the first param, the left and right the second.
655      * @method setPadding
656      * @param {int} iTop    Top pad
657      * @param {int} iRight  Right pad
658      * @param {int} iBot    Bot pad
659      * @param {int} iLeft   Left pad
660      */
661     setPadding: function(iTop, iRight, iBot, iLeft) {
662         // this.padding = [iLeft, iRight, iTop, iBot];
663         if (!iRight && 0 !== iRight) {
664             this.padding = [iTop, iTop, iTop, iTop];
665         } else if (!iBot && 0 !== iBot) {
666             this.padding = [iTop, iRight, iTop, iRight];
667         } else {
668             this.padding = [iTop, iRight, iBot, iLeft];
669         }
670     },
671
672     /**
673      * Stores the initial placement of the linked element.
674      * @method setInitialPosition
675      * @param {int} diffX   the X offset, default 0
676      * @param {int} diffY   the Y offset, default 0
677      */
678     setInitPosition: function(diffX, diffY) {
679         var el = this.getEl();
680
681         if (!this.DDM.verifyEl(el)) {
682             return;
683         }
684
685         var dx = diffX || 0;
686         var dy = diffY || 0;
687
688         var p = Dom.getXY( el );
689
690         this.initPageX = p[0] - dx;
691         this.initPageY = p[1] - dy;
692
693         this.lastPageX = p[0];
694         this.lastPageY = p[1];
695
696
697         this.setStartPosition(p);
698     },
699
700     /**
701      * Sets the start position of the element.  This is set when the obj
702      * is initialized, the reset when a drag is started.
703      * @method setStartPosition
704      * @param pos current position (from previous lookup)
705      * @private
706      */
707     setStartPosition: function(pos) {
708         var p = pos || Dom.getXY( this.getEl() );
709         this.deltaSetXY = null;
710
711         this.startPageX = p[0];
712         this.startPageY = p[1];
713     },
714
715     /**
716      * Add this instance to a group of related drag/drop objects.  All
717      * instances belong to at least one group, and can belong to as many
718      * groups as needed.
719      * @method addToGroup
720      * @param sGroup {string} the name of the group
721      */
722     addToGroup: function(sGroup) {
723         this.groups[sGroup] = true;
724         this.DDM.regDragDrop(this, sGroup);
725     },
726
727     /**
728      * Remove's this instance from the supplied interaction group
729      * @method removeFromGroup
730      * @param {string}  sGroup  The group to drop
731      */
732     removeFromGroup: function(sGroup) {
733         if (this.groups[sGroup]) {
734             delete this.groups[sGroup];
735         }
736
737         this.DDM.removeDDFromGroup(this, sGroup);
738     },
739
740     /**
741      * Allows you to specify that an element other than the linked element
742      * will be moved with the cursor during a drag
743      * @method setDragElId
744      * @param id {string} the id of the element that will be used to initiate the drag
745      */
746     setDragElId: function(id) {
747         this.dragElId = id;
748     },
749
750     /**
751      * Allows you to specify a child of the linked element that should be
752      * used to initiate the drag operation.  An example of this would be if
753      * you have a content div with text and links.  Clicking anywhere in the
754      * content area would normally start the drag operation.  Use this method
755      * to specify that an element inside of the content div is the element
756      * that starts the drag operation.
757      * @method setHandleElId
758      * @param id {string} the id of the element that will be used to
759      * initiate the drag.
760      */
761     setHandleElId: function(id) {
762         if (typeof id !== "string") {
763             id = Roo.id(id);
764         }
765         this.handleElId = id;
766         this.DDM.regHandle(this.id, id);
767     },
768
769     /**
770      * Allows you to set an element outside of the linked element as a drag
771      * handle
772      * @method setOuterHandleElId
773      * @param id the id of the element that will be used to initiate the drag
774      */
775     setOuterHandleElId: function(id) {
776         if (typeof id !== "string") {
777             id = Roo.id(id);
778         }
779         Event.on(id, "mousedown",
780                 this.handleMouseDown, this);
781         this.setHandleElId(id);
782
783         this.hasOuterHandles = true;
784     },
785
786     /**
787      * Remove all drag and drop hooks for this element
788      * @method unreg
789      */
790     unreg: function() {
791         Event.un(this.id, "mousedown",
792                 this.handleMouseDown);
793         this._domRef = null;
794         this.DDM._remove(this);
795     },
796
797     destroy : function(){
798         this.unreg();
799     },
800
801     /**
802      * Returns true if this instance is locked, or the drag drop mgr is locked
803      * (meaning that all drag/drop is disabled on the page.)
804      * @method isLocked
805      * @return {boolean} true if this obj or all drag/drop is locked, else
806      * false
807      */
808     isLocked: function() {
809         return (this.DDM.isLocked() || this.locked);
810     },
811
812     /**
813      * Fired when this object is clicked
814      * @method handleMouseDown
815      * @param {Event} e
816      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
817      * @private
818      */
819     handleMouseDown: function(e, oDD){
820         if (this.primaryButtonOnly && e.button != 0) {
821             return;
822         }
823
824         if (this.isLocked()) {
825             return;
826         }
827
828         this.DDM.refreshCache(this.groups);
829
830         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
831         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
832         } else {
833             if (this.clickValidator(e)) {
834
835                 // set the initial element position
836                 this.setStartPosition();
837
838
839                 this.b4MouseDown(e);
840                 this.onMouseDown(e);
841
842                 this.DDM.handleMouseDown(e, this);
843
844                 this.DDM.stopEvent(e);
845             } else {
846
847
848             }
849         }
850     },
851
852     clickValidator: function(e) {
853         var target = e.getTarget();
854         return ( this.isValidHandleChild(target) &&
855                     (this.id == this.handleElId ||
856                         this.DDM.handleWasClicked(target, this.id)) );
857     },
858
859     /**
860      * Allows you to specify a tag name that should not start a drag operation
861      * when clicked.  This is designed to facilitate embedding links within a
862      * drag handle that do something other than start the drag.
863      * @method addInvalidHandleType
864      * @param {string} tagName the type of element to exclude
865      */
866     addInvalidHandleType: function(tagName) {
867         var type = tagName.toUpperCase();
868         this.invalidHandleTypes[type] = type;
869     },
870
871     /**
872      * Lets you to specify an element id for a child of a drag handle
873      * that should not initiate a drag
874      * @method addInvalidHandleId
875      * @param {string} id the element id of the element you wish to ignore
876      */
877     addInvalidHandleId: function(id) {
878         if (typeof id !== "string") {
879             id = Roo.id(id);
880         }
881         this.invalidHandleIds[id] = id;
882     },
883
884     /**
885      * Lets you specify a css class of elements that will not initiate a drag
886      * @method addInvalidHandleClass
887      * @param {string} cssClass the class of the elements you wish to ignore
888      */
889     addInvalidHandleClass: function(cssClass) {
890         this.invalidHandleClasses.push(cssClass);
891     },
892
893     /**
894      * Unsets an excluded tag name set by addInvalidHandleType
895      * @method removeInvalidHandleType
896      * @param {string} tagName the type of element to unexclude
897      */
898     removeInvalidHandleType: function(tagName) {
899         var type = tagName.toUpperCase();
900         // this.invalidHandleTypes[type] = null;
901         delete this.invalidHandleTypes[type];
902     },
903
904     /**
905      * Unsets an invalid handle id
906      * @method removeInvalidHandleId
907      * @param {string} id the id of the element to re-enable
908      */
909     removeInvalidHandleId: function(id) {
910         if (typeof id !== "string") {
911             id = Roo.id(id);
912         }
913         delete this.invalidHandleIds[id];
914     },
915
916     /**
917      * Unsets an invalid css class
918      * @method removeInvalidHandleClass
919      * @param {string} cssClass the class of the element(s) you wish to
920      * re-enable
921      */
922     removeInvalidHandleClass: function(cssClass) {
923         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
924             if (this.invalidHandleClasses[i] == cssClass) {
925                 delete this.invalidHandleClasses[i];
926             }
927         }
928     },
929
930     /**
931      * Checks the tag exclusion list to see if this click should be ignored
932      * @method isValidHandleChild
933      * @param {HTMLElement} node the HTMLElement to evaluate
934      * @return {boolean} true if this is a valid tag type, false if not
935      */
936     isValidHandleChild: function(node) {
937
938         var valid = true;
939         // var n = (node.nodeName == "#text") ? node.parentNode : node;
940         var nodeName;
941         try {
942             nodeName = node.nodeName.toUpperCase();
943         } catch(e) {
944             nodeName = node.nodeName;
945         }
946         valid = valid && !this.invalidHandleTypes[nodeName];
947         valid = valid && !this.invalidHandleIds[node.id];
948
949         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
950             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
951         }
952
953
954         return valid;
955
956     },
957
958     /**
959      * Create the array of horizontal tick marks if an interval was specified
960      * in setXConstraint().
961      * @method setXTicks
962      * @private
963      */
964     setXTicks: function(iStartX, iTickSize) {
965         this.xTicks = [];
966         this.xTickSize = iTickSize;
967
968         var tickMap = {};
969
970         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
971             if (!tickMap[i]) {
972                 this.xTicks[this.xTicks.length] = i;
973                 tickMap[i] = true;
974             }
975         }
976
977         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
978             if (!tickMap[i]) {
979                 this.xTicks[this.xTicks.length] = i;
980                 tickMap[i] = true;
981             }
982         }
983
984         this.xTicks.sort(this.DDM.numericSort) ;
985     },
986
987     /**
988      * Create the array of vertical tick marks if an interval was specified in
989      * setYConstraint().
990      * @method setYTicks
991      * @private
992      */
993     setYTicks: function(iStartY, iTickSize) {
994         this.yTicks = [];
995         this.yTickSize = iTickSize;
996
997         var tickMap = {};
998
999         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
1000             if (!tickMap[i]) {
1001                 this.yTicks[this.yTicks.length] = i;
1002                 tickMap[i] = true;
1003             }
1004         }
1005
1006         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1007             if (!tickMap[i]) {
1008                 this.yTicks[this.yTicks.length] = i;
1009                 tickMap[i] = true;
1010             }
1011         }
1012
1013         this.yTicks.sort(this.DDM.numericSort) ;
1014     },
1015
1016     /**
1017      * By default, the element can be dragged any place on the screen.  Use
1018      * this method to limit the horizontal travel of the element.  Pass in
1019      * 0,0 for the parameters if you want to lock the drag to the y axis.
1020      * @method setXConstraint
1021      * @param {int} iLeft the number of pixels the element can move to the left
1022      * @param {int} iRight the number of pixels the element can move to the
1023      * right
1024      * @param {int} iTickSize optional parameter for specifying that the
1025      * element
1026      * should move iTickSize pixels at a time.
1027      */
1028     setXConstraint: function(iLeft, iRight, iTickSize) {
1029         this.leftConstraint = iLeft;
1030         this.rightConstraint = iRight;
1031
1032         this.minX = this.initPageX - iLeft;
1033         this.maxX = this.initPageX + iRight;
1034         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1035
1036         this.constrainX = true;
1037     },
1038
1039     /**
1040      * Clears any constraints applied to this instance.  Also clears ticks
1041      * since they can't exist independent of a constraint at this time.
1042      * @method clearConstraints
1043      */
1044     clearConstraints: function() {
1045         this.constrainX = false;
1046         this.constrainY = false;
1047         this.clearTicks();
1048     },
1049
1050     /**
1051      * Clears any tick interval defined for this instance
1052      * @method clearTicks
1053      */
1054     clearTicks: function() {
1055         this.xTicks = null;
1056         this.yTicks = null;
1057         this.xTickSize = 0;
1058         this.yTickSize = 0;
1059     },
1060
1061     /**
1062      * By default, the element can be dragged any place on the screen.  Set
1063      * this to limit the vertical travel of the element.  Pass in 0,0 for the
1064      * parameters if you want to lock the drag to the x axis.
1065      * @method setYConstraint
1066      * @param {int} iUp the number of pixels the element can move up
1067      * @param {int} iDown the number of pixels the element can move down
1068      * @param {int} iTickSize optional parameter for specifying that the
1069      * element should move iTickSize pixels at a time.
1070      */
1071     setYConstraint: function(iUp, iDown, iTickSize) {
1072         this.topConstraint = iUp;
1073         this.bottomConstraint = iDown;
1074
1075         this.minY = this.initPageY - iUp;
1076         this.maxY = this.initPageY + iDown;
1077         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1078
1079         this.constrainY = true;
1080
1081     },
1082
1083     /**
1084      * resetConstraints must be called if you manually reposition a dd element.
1085      * @method resetConstraints
1086      * @param {boolean} maintainOffset
1087      */
1088     resetConstraints: function() {
1089
1090
1091         // Maintain offsets if necessary
1092         if (this.initPageX || this.initPageX === 0) {
1093             // figure out how much this thing has moved
1094             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1095             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1096
1097             this.setInitPosition(dx, dy);
1098
1099         // This is the first time we have detected the element's position
1100         } else {
1101             this.setInitPosition();
1102         }
1103
1104         if (this.constrainX) {
1105             this.setXConstraint( this.leftConstraint,
1106                                  this.rightConstraint,
1107                                  this.xTickSize        );
1108         }
1109
1110         if (this.constrainY) {
1111             this.setYConstraint( this.topConstraint,
1112                                  this.bottomConstraint,
1113                                  this.yTickSize         );
1114         }
1115     },
1116
1117     /**
1118      * Normally the drag element is moved pixel by pixel, but we can specify
1119      * that it move a number of pixels at a time.  This method resolves the
1120      * location when we have it set up like this.
1121      * @method getTick
1122      * @param {int} val where we want to place the object
1123      * @param {int[]} tickArray sorted array of valid points
1124      * @return {int} the closest tick
1125      * @private
1126      */
1127     getTick: function(val, tickArray) {
1128
1129         if (!tickArray) {
1130             // If tick interval is not defined, it is effectively 1 pixel,
1131             // so we return the value passed to us.
1132             return val;
1133         } else if (tickArray[0] >= val) {
1134             // The value is lower than the first tick, so we return the first
1135             // tick.
1136             return tickArray[0];
1137         } else {
1138             for (var i=0, len=tickArray.length; i<len; ++i) {
1139                 var next = i + 1;
1140                 if (tickArray[next] && tickArray[next] >= val) {
1141                     var diff1 = val - tickArray[i];
1142                     var diff2 = tickArray[next] - val;
1143                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1144                 }
1145             }
1146
1147             // The value is larger than the last tick, so we return the last
1148             // tick.
1149             return tickArray[tickArray.length - 1];
1150         }
1151     },
1152
1153     /**
1154      * toString method
1155      * @method toString
1156      * @return {string} string representation of the dd obj
1157      */
1158     toString: function() {
1159         return ("DragDrop " + this.id);
1160     }
1161
1162 });
1163
1164 })();
1165 /*
1166  * Based on:
1167  * Ext JS Library 1.1.1
1168  * Copyright(c) 2006-2007, Ext JS, LLC.
1169  *
1170  * Originally Released Under LGPL - original licence link has changed is not relivant.
1171  *
1172  * Fork - LGPL
1173  * <script type="text/javascript">
1174  */
1175
1176
1177 /**
1178  * The drag and drop utility provides a framework for building drag and drop
1179  * applications.  In addition to enabling drag and drop for specific elements,
1180  * the drag and drop elements are tracked by the manager class, and the
1181  * interactions between the various elements are tracked during the drag and
1182  * the implementing code is notified about these important moments.
1183  */
1184
1185 // Only load the library once.  Rewriting the manager class would orphan
1186 // existing drag and drop instances.
1187 if (!Roo.dd.DragDropMgr) {
1188
1189 /**
1190  * @class Roo.dd.DragDropMgr
1191  * DragDropMgr is a singleton that tracks the element interaction for
1192  * all DragDrop items in the window.  Generally, you will not call
1193  * this class directly, but it does have helper methods that could
1194  * be useful in your DragDrop implementations.
1195  * @singleton
1196  */
1197 Roo.dd.DragDropMgr = function() {
1198
1199     var Event = Roo.EventManager;
1200
1201     return {
1202
1203         /**
1204          * Two dimensional Array of registered DragDrop objects.  The first
1205          * dimension is the DragDrop item group, the second the DragDrop
1206          * object.
1207          * @property ids
1208          * @type {string: string}
1209          * @private
1210          * @static
1211          */
1212         ids: {},
1213
1214         /**
1215          * Array of element ids defined as drag handles.  Used to determine
1216          * if the element that generated the mousedown event is actually the
1217          * handle and not the html element itself.
1218          * @property handleIds
1219          * @type {string: string}
1220          * @private
1221          * @static
1222          */
1223         handleIds: {},
1224
1225         /**
1226          * the DragDrop object that is currently being dragged
1227          * @property dragCurrent
1228          * @type DragDrop
1229          * @private
1230          * @static
1231          **/
1232         dragCurrent: null,
1233
1234         /**
1235          * the DragDrop object(s) that are being hovered over
1236          * @property dragOvers
1237          * @type Array
1238          * @private
1239          * @static
1240          */
1241         dragOvers: {},
1242
1243         /**
1244          * the X distance between the cursor and the object being dragged
1245          * @property deltaX
1246          * @type int
1247          * @private
1248          * @static
1249          */
1250         deltaX: 0,
1251
1252         /**
1253          * the Y distance between the cursor and the object being dragged
1254          * @property deltaY
1255          * @type int
1256          * @private
1257          * @static
1258          */
1259         deltaY: 0,
1260
1261         /**
1262          * Flag to determine if we should prevent the default behavior of the
1263          * events we define. By default this is true, but this can be set to
1264          * false if you need the default behavior (not recommended)
1265          * @property preventDefault
1266          * @type boolean
1267          * @static
1268          */
1269         preventDefault: true,
1270
1271         /**
1272          * Flag to determine if we should stop the propagation of the events
1273          * we generate. This is true by default but you may want to set it to
1274          * false if the html element contains other features that require the
1275          * mouse click.
1276          * @property stopPropagation
1277          * @type boolean
1278          * @static
1279          */
1280         stopPropagation: true,
1281
1282         /**
1283          * Internal flag that is set to true when drag and drop has been
1284          * intialized
1285          * @property initialized
1286          * @private
1287          * @static
1288          */
1289         initalized: false,
1290
1291         /**
1292          * All drag and drop can be disabled.
1293          * @property locked
1294          * @private
1295          * @static
1296          */
1297         locked: false,
1298
1299         /**
1300          * Called the first time an element is registered.
1301          * @method init
1302          * @private
1303          * @static
1304          */
1305         init: function() {
1306             this.initialized = true;
1307         },
1308
1309         /**
1310          * In point mode, drag and drop interaction is defined by the
1311          * location of the cursor during the drag/drop
1312          * @property POINT
1313          * @type int
1314          * @static
1315          */
1316         POINT: 0,
1317
1318         /**
1319          * In intersect mode, drag and drop interactio nis defined by the
1320          * overlap of two or more drag and drop objects.
1321          * @property INTERSECT
1322          * @type int
1323          * @static
1324          */
1325         INTERSECT: 1,
1326
1327         /**
1328          * The current drag and drop mode.  Default: POINT
1329          * @property mode
1330          * @type int
1331          * @static
1332          */
1333         mode: 0,
1334
1335         /**
1336          * Runs method on all drag and drop objects
1337          * @method _execOnAll
1338          * @private
1339          * @static
1340          */
1341         _execOnAll: function(sMethod, args) {
1342             for (var i in this.ids) {
1343                 for (var j in this.ids[i]) {
1344                     var oDD = this.ids[i][j];
1345                     if (! this.isTypeOfDD(oDD)) {
1346                         continue;
1347                     }
1348                     oDD[sMethod].apply(oDD, args);
1349                 }
1350             }
1351         },
1352
1353         /**
1354          * Drag and drop initialization.  Sets up the global event handlers
1355          * @method _onLoad
1356          * @private
1357          * @static
1358          */
1359         _onLoad: function() {
1360
1361             this.init();
1362
1363
1364             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
1365             Event.on(document, "mousemove", this.handleMouseMove, this, true);
1366             Event.on(window,   "unload",    this._onUnload, this, true);
1367             Event.on(window,   "resize",    this._onResize, this, true);
1368             // Event.on(window,   "mouseout",    this._test);
1369
1370         },
1371
1372         /**
1373          * Reset constraints on all drag and drop objs
1374          * @method _onResize
1375          * @private
1376          * @static
1377          */
1378         _onResize: function(e) {
1379             this._execOnAll("resetConstraints", []);
1380         },
1381
1382         /**
1383          * Lock all drag and drop functionality
1384          * @method lock
1385          * @static
1386          */
1387         lock: function() { this.locked = true; },
1388
1389         /**
1390          * Unlock all drag and drop functionality
1391          * @method unlock
1392          * @static
1393          */
1394         unlock: function() { this.locked = false; },
1395
1396         /**
1397          * Is drag and drop locked?
1398          * @method isLocked
1399          * @return {boolean} True if drag and drop is locked, false otherwise.
1400          * @static
1401          */
1402         isLocked: function() { return this.locked; },
1403
1404         /**
1405          * Location cache that is set for all drag drop objects when a drag is
1406          * initiated, cleared when the drag is finished.
1407          * @property locationCache
1408          * @private
1409          * @static
1410          */
1411         locationCache: {},
1412
1413         /**
1414          * Set useCache to false if you want to force object the lookup of each
1415          * drag and drop linked element constantly during a drag.
1416          * @property useCache
1417          * @type boolean
1418          * @static
1419          */
1420         useCache: true,
1421
1422         /**
1423          * The number of pixels that the mouse needs to move after the
1424          * mousedown before the drag is initiated.  Default=3;
1425          * @property clickPixelThresh
1426          * @type int
1427          * @static
1428          */
1429         clickPixelThresh: 3,
1430
1431         /**
1432          * The number of milliseconds after the mousedown event to initiate the
1433          * drag if we don't get a mouseup event. Default=1000
1434          * @property clickTimeThresh
1435          * @type int
1436          * @static
1437          */
1438         clickTimeThresh: 350,
1439
1440         /**
1441          * Flag that indicates that either the drag pixel threshold or the
1442          * mousdown time threshold has been met
1443          * @property dragThreshMet
1444          * @type boolean
1445          * @private
1446          * @static
1447          */
1448         dragThreshMet: false,
1449
1450         /**
1451          * Timeout used for the click time threshold
1452          * @property clickTimeout
1453          * @type Object
1454          * @private
1455          * @static
1456          */
1457         clickTimeout: null,
1458
1459         /**
1460          * The X position of the mousedown event stored for later use when a
1461          * drag threshold is met.
1462          * @property startX
1463          * @type int
1464          * @private
1465          * @static
1466          */
1467         startX: 0,
1468
1469         /**
1470          * The Y position of the mousedown event stored for later use when a
1471          * drag threshold is met.
1472          * @property startY
1473          * @type int
1474          * @private
1475          * @static
1476          */
1477         startY: 0,
1478
1479         /**
1480          * Each DragDrop instance must be registered with the DragDropMgr.
1481          * This is executed in DragDrop.init()
1482          * @method regDragDrop
1483          * @param {DragDrop} oDD the DragDrop object to register
1484          * @param {String} sGroup the name of the group this element belongs to
1485          * @static
1486          */
1487         regDragDrop: function(oDD, sGroup) {
1488             if (!this.initialized) { this.init(); }
1489
1490             if (!this.ids[sGroup]) {
1491                 this.ids[sGroup] = {};
1492             }
1493             this.ids[sGroup][oDD.id] = oDD;
1494         },
1495
1496         /**
1497          * Removes the supplied dd instance from the supplied group. Executed
1498          * by DragDrop.removeFromGroup, so don't call this function directly.
1499          * @method removeDDFromGroup
1500          * @private
1501          * @static
1502          */
1503         removeDDFromGroup: function(oDD, sGroup) {
1504             if (!this.ids[sGroup]) {
1505                 this.ids[sGroup] = {};
1506             }
1507
1508             var obj = this.ids[sGroup];
1509             if (obj && obj[oDD.id]) {
1510                 delete obj[oDD.id];
1511             }
1512         },
1513
1514         /**
1515          * Unregisters a drag and drop item.  This is executed in
1516          * DragDrop.unreg, use that method instead of calling this directly.
1517          * @method _remove
1518          * @private
1519          * @static
1520          */
1521         _remove: function(oDD) {
1522             for (var g in oDD.groups) {
1523                 if (g && this.ids[g][oDD.id]) {
1524                     delete this.ids[g][oDD.id];
1525                 }
1526             }
1527             delete this.handleIds[oDD.id];
1528         },
1529
1530         /**
1531          * Each DragDrop handle element must be registered.  This is done
1532          * automatically when executing DragDrop.setHandleElId()
1533          * @method regHandle
1534          * @param {String} sDDId the DragDrop id this element is a handle for
1535          * @param {String} sHandleId the id of the element that is the drag
1536          * handle
1537          * @static
1538          */
1539         regHandle: function(sDDId, sHandleId) {
1540             if (!this.handleIds[sDDId]) {
1541                 this.handleIds[sDDId] = {};
1542             }
1543             this.handleIds[sDDId][sHandleId] = sHandleId;
1544         },
1545
1546         /**
1547          * Utility function to determine if a given element has been
1548          * registered as a drag drop item.
1549          * @method isDragDrop
1550          * @param {String} id the element id to check
1551          * @return {boolean} true if this element is a DragDrop item,
1552          * false otherwise
1553          * @static
1554          */
1555         isDragDrop: function(id) {
1556             return ( this.getDDById(id) ) ? true : false;
1557         },
1558
1559         /**
1560          * Returns the drag and drop instances that are in all groups the
1561          * passed in instance belongs to.
1562          * @method getRelated
1563          * @param {DragDrop} p_oDD the obj to get related data for
1564          * @param {boolean} bTargetsOnly if true, only return targetable objs
1565          * @return {DragDrop[]} the related instances
1566          * @static
1567          */
1568         getRelated: function(p_oDD, bTargetsOnly) {
1569             var oDDs = [];
1570             for (var i in p_oDD.groups) {
1571                 for (j in this.ids[i]) {
1572                     var dd = this.ids[i][j];
1573                     if (! this.isTypeOfDD(dd)) {
1574                         continue;
1575                     }
1576                     if (!bTargetsOnly || dd.isTarget) {
1577                         oDDs[oDDs.length] = dd;
1578                     }
1579                 }
1580             }
1581
1582             return oDDs;
1583         },
1584
1585         /**
1586          * Returns true if the specified dd target is a legal target for
1587          * the specifice drag obj
1588          * @method isLegalTarget
1589          * @param {DragDrop} the drag obj
1590          * @param {DragDrop} the target
1591          * @return {boolean} true if the target is a legal target for the
1592          * dd obj
1593          * @static
1594          */
1595         isLegalTarget: function (oDD, oTargetDD) {
1596             var targets = this.getRelated(oDD, true);
1597             for (var i=0, len=targets.length;i<len;++i) {
1598                 if (targets[i].id == oTargetDD.id) {
1599                     return true;
1600                 }
1601             }
1602
1603             return false;
1604         },
1605
1606         /**
1607          * My goal is to be able to transparently determine if an object is
1608          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
1609          * returns "object", oDD.constructor.toString() always returns
1610          * "DragDrop" and not the name of the subclass.  So for now it just
1611          * evaluates a well-known variable in DragDrop.
1612          * @method isTypeOfDD
1613          * @param {Object} the object to evaluate
1614          * @return {boolean} true if typeof oDD = DragDrop
1615          * @static
1616          */
1617         isTypeOfDD: function (oDD) {
1618             return (oDD && oDD.__ygDragDrop);
1619         },
1620
1621         /**
1622          * Utility function to determine if a given element has been
1623          * registered as a drag drop handle for the given Drag Drop object.
1624          * @method isHandle
1625          * @param {String} id the element id to check
1626          * @return {boolean} true if this element is a DragDrop handle, false
1627          * otherwise
1628          * @static
1629          */
1630         isHandle: function(sDDId, sHandleId) {
1631             return ( this.handleIds[sDDId] &&
1632                             this.handleIds[sDDId][sHandleId] );
1633         },
1634
1635         /**
1636          * Returns the DragDrop instance for a given id
1637          * @method getDDById
1638          * @param {String} id the id of the DragDrop object
1639          * @return {DragDrop} the drag drop object, null if it is not found
1640          * @static
1641          */
1642         getDDById: function(id) {
1643             for (var i in this.ids) {
1644                 if (this.ids[i][id]) {
1645                     return this.ids[i][id];
1646                 }
1647             }
1648             return null;
1649         },
1650
1651         /**
1652          * Fired after a registered DragDrop object gets the mousedown event.
1653          * Sets up the events required to track the object being dragged
1654          * @method handleMouseDown
1655          * @param {Event} e the event
1656          * @param oDD the DragDrop object being dragged
1657          * @private
1658          * @static
1659          */
1660         handleMouseDown: function(e, oDD) {
1661             if(Roo.QuickTips){
1662                 Roo.QuickTips.disable();
1663             }
1664             this.currentTarget = e.getTarget();
1665
1666             this.dragCurrent = oDD;
1667
1668             var el = oDD.getEl();
1669
1670             // track start position
1671             this.startX = e.getPageX();
1672             this.startY = e.getPageY();
1673
1674             this.deltaX = this.startX - el.offsetLeft;
1675             this.deltaY = this.startY - el.offsetTop;
1676
1677             this.dragThreshMet = false;
1678
1679             this.clickTimeout = setTimeout(
1680                     function() {
1681                         var DDM = Roo.dd.DDM;
1682                         DDM.startDrag(DDM.startX, DDM.startY);
1683                     },
1684                     this.clickTimeThresh );
1685         },
1686
1687         /**
1688          * Fired when either the drag pixel threshol or the mousedown hold
1689          * time threshold has been met.
1690          * @method startDrag
1691          * @param x {int} the X position of the original mousedown
1692          * @param y {int} the Y position of the original mousedown
1693          * @static
1694          */
1695         startDrag: function(x, y) {
1696             clearTimeout(this.clickTimeout);
1697             if (this.dragCurrent) {
1698                 this.dragCurrent.b4StartDrag(x, y);
1699                 this.dragCurrent.startDrag(x, y);
1700             }
1701             this.dragThreshMet = true;
1702         },
1703
1704         /**
1705          * Internal function to handle the mouseup event.  Will be invoked
1706          * from the context of the document.
1707          * @method handleMouseUp
1708          * @param {Event} e the event
1709          * @private
1710          * @static
1711          */
1712         handleMouseUp: function(e) {
1713
1714             if(Roo.QuickTips){
1715                 Roo.QuickTips.enable();
1716             }
1717             if (! this.dragCurrent) {
1718                 return;
1719             }
1720
1721             clearTimeout(this.clickTimeout);
1722
1723             if (this.dragThreshMet) {
1724                 this.fireEvents(e, true);
1725             } else {
1726             }
1727
1728             this.stopDrag(e);
1729
1730             this.stopEvent(e);
1731         },
1732
1733         /**
1734          * Utility to stop event propagation and event default, if these
1735          * features are turned on.
1736          * @method stopEvent
1737          * @param {Event} e the event as returned by this.getEvent()
1738          * @static
1739          */
1740         stopEvent: function(e){
1741             if(this.stopPropagation) {
1742                 e.stopPropagation();
1743             }
1744
1745             if (this.preventDefault) {
1746                 e.preventDefault();
1747             }
1748         },
1749
1750         /**
1751          * Internal function to clean up event handlers after the drag
1752          * operation is complete
1753          * @method stopDrag
1754          * @param {Event} e the event
1755          * @private
1756          * @static
1757          */
1758         stopDrag: function(e) {
1759             // Fire the drag end event for the item that was dragged
1760             if (this.dragCurrent) {
1761                 if (this.dragThreshMet) {
1762                     this.dragCurrent.b4EndDrag(e);
1763                     this.dragCurrent.endDrag(e);
1764                 }
1765
1766                 this.dragCurrent.onMouseUp(e);
1767             }
1768
1769             this.dragCurrent = null;
1770             this.dragOvers = {};
1771         },
1772
1773         /**
1774          * Internal function to handle the mousemove event.  Will be invoked
1775          * from the context of the html element.
1776          *
1777          * @TODO figure out what we can do about mouse events lost when the
1778          * user drags objects beyond the window boundary.  Currently we can
1779          * detect this in internet explorer by verifying that the mouse is
1780          * down during the mousemove event.  Firefox doesn't give us the
1781          * button state on the mousemove event.
1782          * @method handleMouseMove
1783          * @param {Event} e the event
1784          * @private
1785          * @static
1786          */
1787         handleMouseMove: function(e) {
1788             if (! this.dragCurrent) {
1789                 return true;
1790             }
1791
1792             // var button = e.which || e.button;
1793
1794             // check for IE mouseup outside of page boundary
1795             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1796                 this.stopEvent(e);
1797                 return this.handleMouseUp(e);
1798             }
1799
1800             if (!this.dragThreshMet) {
1801                 var diffX = Math.abs(this.startX - e.getPageX());
1802                 var diffY = Math.abs(this.startY - e.getPageY());
1803                 if (diffX > this.clickPixelThresh ||
1804                             diffY > this.clickPixelThresh) {
1805                     this.startDrag(this.startX, this.startY);
1806                 }
1807             }
1808
1809             if (this.dragThreshMet) {
1810                 this.dragCurrent.b4Drag(e);
1811                 this.dragCurrent.onDrag(e);
1812                 if(!this.dragCurrent.moveOnly){
1813                     this.fireEvents(e, false);
1814                 }
1815             }
1816
1817             this.stopEvent(e);
1818
1819             return true;
1820         },
1821
1822         /**
1823          * Iterates over all of the DragDrop elements to find ones we are
1824          * hovering over or dropping on
1825          * @method fireEvents
1826          * @param {Event} e the event
1827          * @param {boolean} isDrop is this a drop op or a mouseover op?
1828          * @private
1829          * @static
1830          */
1831         fireEvents: function(e, isDrop) {
1832             var dc = this.dragCurrent;
1833
1834             // If the user did the mouse up outside of the window, we could
1835             // get here even though we have ended the drag.
1836             if (!dc || dc.isLocked()) {
1837                 return;
1838             }
1839
1840             var pt = e.getPoint();
1841
1842             // cache the previous dragOver array
1843             var oldOvers = [];
1844
1845             var outEvts   = [];
1846             var overEvts  = [];
1847             var dropEvts  = [];
1848             var enterEvts = [];
1849
1850             // Check to see if the object(s) we were hovering over is no longer
1851             // being hovered over so we can fire the onDragOut event
1852             for (var i in this.dragOvers) {
1853
1854                 var ddo = this.dragOvers[i];
1855
1856                 if (! this.isTypeOfDD(ddo)) {
1857                     continue;
1858                 }
1859
1860                 if (! this.isOverTarget(pt, ddo, this.mode)) {
1861                     outEvts.push( ddo );
1862                 }
1863
1864                 oldOvers[i] = true;
1865                 delete this.dragOvers[i];
1866             }
1867
1868             for (var sGroup in dc.groups) {
1869
1870                 if ("string" != typeof sGroup) {
1871                     continue;
1872                 }
1873
1874                 for (i in this.ids[sGroup]) {
1875                     var oDD = this.ids[sGroup][i];
1876                     if (! this.isTypeOfDD(oDD)) {
1877                         continue;
1878                     }
1879
1880                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1881                         if (this.isOverTarget(pt, oDD, this.mode)) {
1882                             // look for drop interactions
1883                             if (isDrop) {
1884                                 dropEvts.push( oDD );
1885                             // look for drag enter and drag over interactions
1886                             } else {
1887
1888                                 // initial drag over: dragEnter fires
1889                                 if (!oldOvers[oDD.id]) {
1890                                     enterEvts.push( oDD );
1891                                 // subsequent drag overs: dragOver fires
1892                                 } else {
1893                                     overEvts.push( oDD );
1894                                 }
1895
1896                                 this.dragOvers[oDD.id] = oDD;
1897                             }
1898                         }
1899                     }
1900                 }
1901             }
1902
1903             if (this.mode) {
1904                 if (outEvts.length) {
1905                     dc.b4DragOut(e, outEvts);
1906                     dc.onDragOut(e, outEvts);
1907                 }
1908
1909                 if (enterEvts.length) {
1910                     dc.onDragEnter(e, enterEvts);
1911                 }
1912
1913                 if (overEvts.length) {
1914                     dc.b4DragOver(e, overEvts);
1915                     dc.onDragOver(e, overEvts);
1916                 }
1917
1918                 if (dropEvts.length) {
1919                     dc.b4DragDrop(e, dropEvts);
1920                     dc.onDragDrop(e, dropEvts);
1921                 }
1922
1923             } else {
1924                 // fire dragout events
1925                 var len = 0;
1926                 for (i=0, len=outEvts.length; i<len; ++i) {
1927                     dc.b4DragOut(e, outEvts[i].id);
1928                     dc.onDragOut(e, outEvts[i].id);
1929                 }
1930
1931                 // fire enter events
1932                 for (i=0,len=enterEvts.length; i<len; ++i) {
1933                     // dc.b4DragEnter(e, oDD.id);
1934                     dc.onDragEnter(e, enterEvts[i].id);
1935                 }
1936
1937                 // fire over events
1938                 for (i=0,len=overEvts.length; i<len; ++i) {
1939                     dc.b4DragOver(e, overEvts[i].id);
1940                     dc.onDragOver(e, overEvts[i].id);
1941                 }
1942
1943                 // fire drop events
1944                 for (i=0, len=dropEvts.length; i<len; ++i) {
1945                     dc.b4DragDrop(e, dropEvts[i].id);
1946                     dc.onDragDrop(e, dropEvts[i].id);
1947                 }
1948
1949             }
1950
1951             // notify about a drop that did not find a target
1952             if (isDrop && !dropEvts.length) {
1953                 dc.onInvalidDrop(e);
1954             }
1955
1956         },
1957
1958         /**
1959          * Helper function for getting the best match from the list of drag
1960          * and drop objects returned by the drag and drop events when we are
1961          * in INTERSECT mode.  It returns either the first object that the
1962          * cursor is over, or the object that has the greatest overlap with
1963          * the dragged element.
1964          * @method getBestMatch
1965          * @param  {DragDrop[]} dds The array of drag and drop objects
1966          * targeted
1967          * @return {DragDrop}       The best single match
1968          * @static
1969          */
1970         getBestMatch: function(dds) {
1971             var winner = null;
1972             // Return null if the input is not what we expect
1973             //if (!dds || !dds.length || dds.length == 0) {
1974                // winner = null;
1975             // If there is only one item, it wins
1976             //} else if (dds.length == 1) {
1977
1978             var len = dds.length;
1979
1980             if (len == 1) {
1981                 winner = dds[0];
1982             } else {
1983                 // Loop through the targeted items
1984                 for (var i=0; i<len; ++i) {
1985                     var dd = dds[i];
1986                     // If the cursor is over the object, it wins.  If the
1987                     // cursor is over multiple matches, the first one we come
1988                     // to wins.
1989                     if (dd.cursorIsOver) {
1990                         winner = dd;
1991                         break;
1992                     // Otherwise the object with the most overlap wins
1993                     } else {
1994                         if (!winner ||
1995                             winner.overlap.getArea() < dd.overlap.getArea()) {
1996                             winner = dd;
1997                         }
1998                     }
1999                 }
2000             }
2001
2002             return winner;
2003         },
2004
2005         /**
2006          * Refreshes the cache of the top-left and bottom-right points of the
2007          * drag and drop objects in the specified group(s).  This is in the
2008          * format that is stored in the drag and drop instance, so typical
2009          * usage is:
2010          * <code>
2011          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2012          * </code>
2013          * Alternatively:
2014          * <code>
2015          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2016          * </code>
2017          * @TODO this really should be an indexed array.  Alternatively this
2018          * method could accept both.
2019          * @method refreshCache
2020          * @param {Object} groups an associative array of groups to refresh
2021          * @static
2022          */
2023         refreshCache: function(groups) {
2024             for (var sGroup in groups) {
2025                 if ("string" != typeof sGroup) {
2026                     continue;
2027                 }
2028                 for (var i in this.ids[sGroup]) {
2029                     var oDD = this.ids[sGroup][i];
2030
2031                     if (this.isTypeOfDD(oDD)) {
2032                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2033                         var loc = this.getLocation(oDD);
2034                         if (loc) {
2035                             this.locationCache[oDD.id] = loc;
2036                         } else {
2037                             delete this.locationCache[oDD.id];
2038                             // this will unregister the drag and drop object if
2039                             // the element is not in a usable state
2040                             // oDD.unreg();
2041                         }
2042                     }
2043                 }
2044             }
2045         },
2046
2047         /**
2048          * This checks to make sure an element exists and is in the DOM.  The
2049          * main purpose is to handle cases where innerHTML is used to remove
2050          * drag and drop objects from the DOM.  IE provides an 'unspecified
2051          * error' when trying to access the offsetParent of such an element
2052          * @method verifyEl
2053          * @param {HTMLElement} el the element to check
2054          * @return {boolean} true if the element looks usable
2055          * @static
2056          */
2057         verifyEl: function(el) {
2058             if (el) {
2059                 var parent;
2060                 if(Roo.isIE){
2061                     try{
2062                         parent = el.offsetParent;
2063                     }catch(e){}
2064                 }else{
2065                     parent = el.offsetParent;
2066                 }
2067                 if (parent) {
2068                     return true;
2069                 }
2070             }
2071
2072             return false;
2073         },
2074
2075         /**
2076          * Returns a Region object containing the drag and drop element's position
2077          * and size, including the padding configured for it
2078          * @method getLocation
2079          * @param {DragDrop} oDD the drag and drop object to get the
2080          *                       location for
2081          * @return {Roo.lib.Region} a Region object representing the total area
2082          *                             the element occupies, including any padding
2083          *                             the instance is configured for.
2084          * @static
2085          */
2086         getLocation: function(oDD) {
2087             if (! this.isTypeOfDD(oDD)) {
2088                 return null;
2089             }
2090
2091             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2092
2093             try {
2094                 pos= Roo.lib.Dom.getXY(el);
2095             } catch (e) { }
2096
2097             if (!pos) {
2098                 return null;
2099             }
2100
2101             x1 = pos[0];
2102             x2 = x1 + el.offsetWidth;
2103             y1 = pos[1];
2104             y2 = y1 + el.offsetHeight;
2105
2106             t = y1 - oDD.padding[0];
2107             r = x2 + oDD.padding[1];
2108             b = y2 + oDD.padding[2];
2109             l = x1 - oDD.padding[3];
2110
2111             return new Roo.lib.Region( t, r, b, l );
2112         },
2113
2114         /**
2115          * Checks the cursor location to see if it over the target
2116          * @method isOverTarget
2117          * @param {Roo.lib.Point} pt The point to evaluate
2118          * @param {DragDrop} oTarget the DragDrop object we are inspecting
2119          * @return {boolean} true if the mouse is over the target
2120          * @private
2121          * @static
2122          */
2123         isOverTarget: function(pt, oTarget, intersect) {
2124             // use cache if available
2125             var loc = this.locationCache[oTarget.id];
2126             if (!loc || !this.useCache) {
2127                 loc = this.getLocation(oTarget);
2128                 this.locationCache[oTarget.id] = loc;
2129
2130             }
2131
2132             if (!loc) {
2133                 return false;
2134             }
2135
2136             oTarget.cursorIsOver = loc.contains( pt );
2137
2138             // DragDrop is using this as a sanity check for the initial mousedown
2139             // in this case we are done.  In POINT mode, if the drag obj has no
2140             // contraints, we are also done. Otherwise we need to evaluate the
2141             // location of the target as related to the actual location of the
2142             // dragged element.
2143             var dc = this.dragCurrent;
2144             if (!dc || !dc.getTargetCoord ||
2145                     (!intersect && !dc.constrainX && !dc.constrainY)) {
2146                 return oTarget.cursorIsOver;
2147             }
2148
2149             oTarget.overlap = null;
2150
2151             // Get the current location of the drag element, this is the
2152             // location of the mouse event less the delta that represents
2153             // where the original mousedown happened on the element.  We
2154             // need to consider constraints and ticks as well.
2155             var pos = dc.getTargetCoord(pt.x, pt.y);
2156
2157             var el = dc.getDragEl();
2158             var curRegion = new Roo.lib.Region( pos.y,
2159                                                    pos.x + el.offsetWidth,
2160                                                    pos.y + el.offsetHeight,
2161                                                    pos.x );
2162
2163             var overlap = curRegion.intersect(loc);
2164
2165             if (overlap) {
2166                 oTarget.overlap = overlap;
2167                 return (intersect) ? true : oTarget.cursorIsOver;
2168             } else {
2169                 return false;
2170             }
2171         },
2172
2173         /**
2174          * unload event handler
2175          * @method _onUnload
2176          * @private
2177          * @static
2178          */
2179         _onUnload: function(e, me) {
2180             Roo.dd.DragDropMgr.unregAll();
2181         },
2182
2183         /**
2184          * Cleans up the drag and drop events and objects.
2185          * @method unregAll
2186          * @private
2187          * @static
2188          */
2189         unregAll: function() {
2190
2191             if (this.dragCurrent) {
2192                 this.stopDrag();
2193                 this.dragCurrent = null;
2194             }
2195
2196             this._execOnAll("unreg", []);
2197
2198             for (i in this.elementCache) {
2199                 delete this.elementCache[i];
2200             }
2201
2202             this.elementCache = {};
2203             this.ids = {};
2204         },
2205
2206         /**
2207          * A cache of DOM elements
2208          * @property elementCache
2209          * @private
2210          * @static
2211          */
2212         elementCache: {},
2213
2214         /**
2215          * Get the wrapper for the DOM element specified
2216          * @method getElWrapper
2217          * @param {String} id the id of the element to get
2218          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2219          * @private
2220          * @deprecated This wrapper isn't that useful
2221          * @static
2222          */
2223         getElWrapper: function(id) {
2224             var oWrapper = this.elementCache[id];
2225             if (!oWrapper || !oWrapper.el) {
2226                 oWrapper = this.elementCache[id] =
2227                     new this.ElementWrapper(Roo.getDom(id));
2228             }
2229             return oWrapper;
2230         },
2231
2232         /**
2233          * Returns the actual DOM element
2234          * @method getElement
2235          * @param {String} id the id of the elment to get
2236          * @return {Object} The element
2237          * @deprecated use Roo.getDom instead
2238          * @static
2239          */
2240         getElement: function(id) {
2241             return Roo.getDom(id);
2242         },
2243
2244         /**
2245          * Returns the style property for the DOM element (i.e.,
2246          * document.getElById(id).style)
2247          * @method getCss
2248          * @param {String} id the id of the elment to get
2249          * @return {Object} The style property of the element
2250          * @deprecated use Roo.getDom instead
2251          * @static
2252          */
2253         getCss: function(id) {
2254             var el = Roo.getDom(id);
2255             return (el) ? el.style : null;
2256         },
2257
2258         /**
2259          * Inner class for cached elements
2260          * @class DragDropMgr.ElementWrapper
2261          * @for DragDropMgr
2262          * @private
2263          * @deprecated
2264          */
2265         ElementWrapper: function(el) {
2266                 /**
2267                  * The element
2268                  * @property el
2269                  */
2270                 this.el = el || null;
2271                 /**
2272                  * The element id
2273                  * @property id
2274                  */
2275                 this.id = this.el && el.id;
2276                 /**
2277                  * A reference to the style property
2278                  * @property css
2279                  */
2280                 this.css = this.el && el.style;
2281             },
2282
2283         /**
2284          * Returns the X position of an html element
2285          * @method getPosX
2286          * @param el the element for which to get the position
2287          * @return {int} the X coordinate
2288          * @for DragDropMgr
2289          * @deprecated use Roo.lib.Dom.getX instead
2290          * @static
2291          */
2292         getPosX: function(el) {
2293             return Roo.lib.Dom.getX(el);
2294         },
2295
2296         /**
2297          * Returns the Y position of an html element
2298          * @method getPosY
2299          * @param el the element for which to get the position
2300          * @return {int} the Y coordinate
2301          * @deprecated use Roo.lib.Dom.getY instead
2302          * @static
2303          */
2304         getPosY: function(el) {
2305             return Roo.lib.Dom.getY(el);
2306         },
2307
2308         /**
2309          * Swap two nodes.  In IE, we use the native method, for others we
2310          * emulate the IE behavior
2311          * @method swapNode
2312          * @param n1 the first node to swap
2313          * @param n2 the other node to swap
2314          * @static
2315          */
2316         swapNode: function(n1, n2) {
2317             if (n1.swapNode) {
2318                 n1.swapNode(n2);
2319             } else {
2320                 var p = n2.parentNode;
2321                 var s = n2.nextSibling;
2322
2323                 if (s == n1) {
2324                     p.insertBefore(n1, n2);
2325                 } else if (n2 == n1.nextSibling) {
2326                     p.insertBefore(n2, n1);
2327                 } else {
2328                     n1.parentNode.replaceChild(n2, n1);
2329                     p.insertBefore(n1, s);
2330                 }
2331             }
2332         },
2333
2334         /**
2335          * Returns the current scroll position
2336          * @method getScroll
2337          * @private
2338          * @static
2339          */
2340         getScroll: function () {
2341             var t, l, dde=document.documentElement, db=document.body;
2342             if (dde && (dde.scrollTop || dde.scrollLeft)) {
2343                 t = dde.scrollTop;
2344                 l = dde.scrollLeft;
2345             } else if (db) {
2346                 t = db.scrollTop;
2347                 l = db.scrollLeft;
2348             } else {
2349
2350             }
2351             return { top: t, left: l };
2352         },
2353
2354         /**
2355          * Returns the specified element style property
2356          * @method getStyle
2357          * @param {HTMLElement} el          the element
2358          * @param {string}      styleProp   the style property
2359          * @return {string} The value of the style property
2360          * @deprecated use Roo.lib.Dom.getStyle
2361          * @static
2362          */
2363         getStyle: function(el, styleProp) {
2364             return Roo.fly(el).getStyle(styleProp);
2365         },
2366
2367         /**
2368          * Gets the scrollTop
2369          * @method getScrollTop
2370          * @return {int} the document's scrollTop
2371          * @static
2372          */
2373         getScrollTop: function () { return this.getScroll().top; },
2374
2375         /**
2376          * Gets the scrollLeft
2377          * @method getScrollLeft
2378          * @return {int} the document's scrollTop
2379          * @static
2380          */
2381         getScrollLeft: function () { return this.getScroll().left; },
2382
2383         /**
2384          * Sets the x/y position of an element to the location of the
2385          * target element.
2386          * @method moveToEl
2387          * @param {HTMLElement} moveEl      The element to move
2388          * @param {HTMLElement} targetEl    The position reference element
2389          * @static
2390          */
2391         moveToEl: function (moveEl, targetEl) {
2392             var aCoord = Roo.lib.Dom.getXY(targetEl);
2393             Roo.lib.Dom.setXY(moveEl, aCoord);
2394         },
2395
2396         /**
2397          * Numeric array sort function
2398          * @method numericSort
2399          * @static
2400          */
2401         numericSort: function(a, b) { return (a - b); },
2402
2403         /**
2404          * Internal counter
2405          * @property _timeoutCount
2406          * @private
2407          * @static
2408          */
2409         _timeoutCount: 0,
2410
2411         /**
2412          * Trying to make the load order less important.  Without this we get
2413          * an error if this file is loaded before the Event Utility.
2414          * @method _addListeners
2415          * @private
2416          * @static
2417          */
2418         _addListeners: function() {
2419             var DDM = Roo.dd.DDM;
2420             if ( Roo.lib.Event && document ) {
2421                 DDM._onLoad();
2422             } else {
2423                 if (DDM._timeoutCount > 2000) {
2424                 } else {
2425                     setTimeout(DDM._addListeners, 10);
2426                     if (document && document.body) {
2427                         DDM._timeoutCount += 1;
2428                     }
2429                 }
2430             }
2431         },
2432
2433         /**
2434          * Recursively searches the immediate parent and all child nodes for
2435          * the handle element in order to determine wheter or not it was
2436          * clicked.
2437          * @method handleWasClicked
2438          * @param node the html element to inspect
2439          * @static
2440          */
2441         handleWasClicked: function(node, id) {
2442             if (this.isHandle(id, node.id)) {
2443                 return true;
2444             } else {
2445                 // check to see if this is a text node child of the one we want
2446                 var p = node.parentNode;
2447
2448                 while (p) {
2449                     if (this.isHandle(id, p.id)) {
2450                         return true;
2451                     } else {
2452                         p = p.parentNode;
2453                     }
2454                 }
2455             }
2456
2457             return false;
2458         }
2459
2460     };
2461
2462 }();
2463
2464 // shorter alias, save a few bytes
2465 Roo.dd.DDM = Roo.dd.DragDropMgr;
2466 Roo.dd.DDM._addListeners();
2467
2468 }/*
2469  * Based on:
2470  * Ext JS Library 1.1.1
2471  * Copyright(c) 2006-2007, Ext JS, LLC.
2472  *
2473  * Originally Released Under LGPL - original licence link has changed is not relivant.
2474  *
2475  * Fork - LGPL
2476  * <script type="text/javascript">
2477  */
2478
2479 /**
2480  * @class Roo.dd.DD
2481  * A DragDrop implementation where the linked element follows the
2482  * mouse cursor during a drag.
2483  * @extends Roo.dd.DragDrop
2484  * @constructor
2485  * @param {String} id the id of the linked element
2486  * @param {String} sGroup the group of related DragDrop items
2487  * @param {object} config an object containing configurable attributes
2488  *                Valid properties for DD:
2489  *                    scroll
2490  */
2491 Roo.dd.DD = function(id, sGroup, config) {
2492     if (id) {
2493         this.init(id, sGroup, config);
2494     }
2495 };
2496
2497 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2498
2499     /**
2500      * When set to true, the utility automatically tries to scroll the browser
2501      * window wehn a drag and drop element is dragged near the viewport boundary.
2502      * Defaults to true.
2503      * @property scroll
2504      * @type boolean
2505      */
2506     scroll: true,
2507
2508     /**
2509      * Sets the pointer offset to the distance between the linked element's top
2510      * left corner and the location the element was clicked
2511      * @method autoOffset
2512      * @param {int} iPageX the X coordinate of the click
2513      * @param {int} iPageY the Y coordinate of the click
2514      */
2515     autoOffset: function(iPageX, iPageY) {
2516         var x = iPageX - this.startPageX;
2517         var y = iPageY - this.startPageY;
2518         this.setDelta(x, y);
2519     },
2520
2521     /**
2522      * Sets the pointer offset.  You can call this directly to force the
2523      * offset to be in a particular location (e.g., pass in 0,0 to set it
2524      * to the center of the object)
2525      * @method setDelta
2526      * @param {int} iDeltaX the distance from the left
2527      * @param {int} iDeltaY the distance from the top
2528      */
2529     setDelta: function(iDeltaX, iDeltaY) {
2530         this.deltaX = iDeltaX;
2531         this.deltaY = iDeltaY;
2532     },
2533
2534     /**
2535      * Sets the drag element to the location of the mousedown or click event,
2536      * maintaining the cursor location relative to the location on the element
2537      * that was clicked.  Override this if you want to place the element in a
2538      * location other than where the cursor is.
2539      * @method setDragElPos
2540      * @param {int} iPageX the X coordinate of the mousedown or drag event
2541      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2542      */
2543     setDragElPos: function(iPageX, iPageY) {
2544         // the first time we do this, we are going to check to make sure
2545         // the element has css positioning
2546
2547         var el = this.getDragEl();
2548         this.alignElWithMouse(el, iPageX, iPageY);
2549     },
2550
2551     /**
2552      * Sets the element to the location of the mousedown or click event,
2553      * maintaining the cursor location relative to the location on the element
2554      * that was clicked.  Override this if you want to place the element in a
2555      * location other than where the cursor is.
2556      * @method alignElWithMouse
2557      * @param {HTMLElement} el the element to move
2558      * @param {int} iPageX the X coordinate of the mousedown or drag event
2559      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2560      */
2561     alignElWithMouse: function(el, iPageX, iPageY) {
2562         var oCoord = this.getTargetCoord(iPageX, iPageY);
2563         var fly = el.dom ? el : Roo.fly(el);
2564         if (!this.deltaSetXY) {
2565             var aCoord = [oCoord.x, oCoord.y];
2566             fly.setXY(aCoord);
2567             var newLeft = fly.getLeft(true);
2568             var newTop  = fly.getTop(true);
2569             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2570         } else {
2571             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2572         }
2573
2574         this.cachePosition(oCoord.x, oCoord.y);
2575         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2576         return oCoord;
2577     },
2578
2579     /**
2580      * Saves the most recent position so that we can reset the constraints and
2581      * tick marks on-demand.  We need to know this so that we can calculate the
2582      * number of pixels the element is offset from its original position.
2583      * @method cachePosition
2584      * @param iPageX the current x position (optional, this just makes it so we
2585      * don't have to look it up again)
2586      * @param iPageY the current y position (optional, this just makes it so we
2587      * don't have to look it up again)
2588      */
2589     cachePosition: function(iPageX, iPageY) {
2590         if (iPageX) {
2591             this.lastPageX = iPageX;
2592             this.lastPageY = iPageY;
2593         } else {
2594             var aCoord = Roo.lib.Dom.getXY(this.getEl());
2595             this.lastPageX = aCoord[0];
2596             this.lastPageY = aCoord[1];
2597         }
2598     },
2599
2600     /**
2601      * Auto-scroll the window if the dragged object has been moved beyond the
2602      * visible window boundary.
2603      * @method autoScroll
2604      * @param {int} x the drag element's x position
2605      * @param {int} y the drag element's y position
2606      * @param {int} h the height of the drag element
2607      * @param {int} w the width of the drag element
2608      * @private
2609      */
2610     autoScroll: function(x, y, h, w) {
2611
2612         if (this.scroll) {
2613             // The client height
2614             var clientH = Roo.lib.Dom.getViewWidth();
2615
2616             // The client width
2617             var clientW = Roo.lib.Dom.getViewHeight();
2618
2619             // The amt scrolled down
2620             var st = this.DDM.getScrollTop();
2621
2622             // The amt scrolled right
2623             var sl = this.DDM.getScrollLeft();
2624
2625             // Location of the bottom of the element
2626             var bot = h + y;
2627
2628             // Location of the right of the element
2629             var right = w + x;
2630
2631             // The distance from the cursor to the bottom of the visible area,
2632             // adjusted so that we don't scroll if the cursor is beyond the
2633             // element drag constraints
2634             var toBot = (clientH + st - y - this.deltaY);
2635
2636             // The distance from the cursor to the right of the visible area
2637             var toRight = (clientW + sl - x - this.deltaX);
2638
2639
2640             // How close to the edge the cursor must be before we scroll
2641             // var thresh = (document.all) ? 100 : 40;
2642             var thresh = 40;
2643
2644             // How many pixels to scroll per autoscroll op.  This helps to reduce
2645             // clunky scrolling. IE is more sensitive about this ... it needs this
2646             // value to be higher.
2647             var scrAmt = (document.all) ? 80 : 30;
2648
2649             // Scroll down if we are near the bottom of the visible page and the
2650             // obj extends below the crease
2651             if ( bot > clientH && toBot < thresh ) {
2652                 window.scrollTo(sl, st + scrAmt);
2653             }
2654
2655             // Scroll up if the window is scrolled down and the top of the object
2656             // goes above the top border
2657             if ( y < st && st > 0 && y - st < thresh ) {
2658                 window.scrollTo(sl, st - scrAmt);
2659             }
2660
2661             // Scroll right if the obj is beyond the right border and the cursor is
2662             // near the border.
2663             if ( right > clientW && toRight < thresh ) {
2664                 window.scrollTo(sl + scrAmt, st);
2665             }
2666
2667             // Scroll left if the window has been scrolled to the right and the obj
2668             // extends past the left border
2669             if ( x < sl && sl > 0 && x - sl < thresh ) {
2670                 window.scrollTo(sl - scrAmt, st);
2671             }
2672         }
2673     },
2674
2675     /**
2676      * Finds the location the element should be placed if we want to move
2677      * it to where the mouse location less the click offset would place us.
2678      * @method getTargetCoord
2679      * @param {int} iPageX the X coordinate of the click
2680      * @param {int} iPageY the Y coordinate of the click
2681      * @return an object that contains the coordinates (Object.x and Object.y)
2682      * @private
2683      */
2684     getTargetCoord: function(iPageX, iPageY) {
2685
2686
2687         var x = iPageX - this.deltaX;
2688         var y = iPageY - this.deltaY;
2689
2690         if (this.constrainX) {
2691             if (x < this.minX) { x = this.minX; }
2692             if (x > this.maxX) { x = this.maxX; }
2693         }
2694
2695         if (this.constrainY) {
2696             if (y < this.minY) { y = this.minY; }
2697             if (y > this.maxY) { y = this.maxY; }
2698         }
2699
2700         x = this.getTick(x, this.xTicks);
2701         y = this.getTick(y, this.yTicks);
2702
2703
2704         return {x:x, y:y};
2705     },
2706
2707     /*
2708      * Sets up config options specific to this class. Overrides
2709      * Roo.dd.DragDrop, but all versions of this method through the
2710      * inheritance chain are called
2711      */
2712     applyConfig: function() {
2713         Roo.dd.DD.superclass.applyConfig.call(this);
2714         this.scroll = (this.config.scroll !== false);
2715     },
2716
2717     /*
2718      * Event that fires prior to the onMouseDown event.  Overrides
2719      * Roo.dd.DragDrop.
2720      */
2721     b4MouseDown: function(e) {
2722         // this.resetConstraints();
2723         this.autoOffset(e.getPageX(),
2724                             e.getPageY());
2725     },
2726
2727     /*
2728      * Event that fires prior to the onDrag event.  Overrides
2729      * Roo.dd.DragDrop.
2730      */
2731     b4Drag: function(e) {
2732         this.setDragElPos(e.getPageX(),
2733                             e.getPageY());
2734     },
2735
2736     toString: function() {
2737         return ("DD " + this.id);
2738     }
2739
2740     //////////////////////////////////////////////////////////////////////////
2741     // Debugging ygDragDrop events that can be overridden
2742     //////////////////////////////////////////////////////////////////////////
2743     /*
2744     startDrag: function(x, y) {
2745     },
2746
2747     onDrag: function(e) {
2748     },
2749
2750     onDragEnter: function(e, id) {
2751     },
2752
2753     onDragOver: function(e, id) {
2754     },
2755
2756     onDragOut: function(e, id) {
2757     },
2758
2759     onDragDrop: function(e, id) {
2760     },
2761
2762     endDrag: function(e) {
2763     }
2764
2765     */
2766
2767 });/*
2768  * Based on:
2769  * Ext JS Library 1.1.1
2770  * Copyright(c) 2006-2007, Ext JS, LLC.
2771  *
2772  * Originally Released Under LGPL - original licence link has changed is not relivant.
2773  *
2774  * Fork - LGPL
2775  * <script type="text/javascript">
2776  */
2777
2778 /**
2779  * @class Roo.dd.DDProxy
2780  * A DragDrop implementation that inserts an empty, bordered div into
2781  * the document that follows the cursor during drag operations.  At the time of
2782  * the click, the frame div is resized to the dimensions of the linked html
2783  * element, and moved to the exact location of the linked element.
2784  *
2785  * References to the "frame" element refer to the single proxy element that
2786  * was created to be dragged in place of all DDProxy elements on the
2787  * page.
2788  *
2789  * @extends Roo.dd.DD
2790  * @constructor
2791  * @param {String} id the id of the linked html element
2792  * @param {String} sGroup the group of related DragDrop objects
2793  * @param {object} config an object containing configurable attributes
2794  *                Valid properties for DDProxy in addition to those in DragDrop:
2795  *                   resizeFrame, centerFrame, dragElId
2796  */
2797 Roo.dd.DDProxy = function(id, sGroup, config) {
2798     if (id) {
2799         this.init(id, sGroup, config);
2800         this.initFrame();
2801     }
2802 };
2803
2804 /**
2805  * The default drag frame div id
2806  * @property Roo.dd.DDProxy.dragElId
2807  * @type String
2808  * @static
2809  */
2810 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2811
2812 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2813
2814     /**
2815      * By default we resize the drag frame to be the same size as the element
2816      * we want to drag (this is to get the frame effect).  We can turn it off
2817      * if we want a different behavior.
2818      * @property resizeFrame
2819      * @type boolean
2820      */
2821     resizeFrame: true,
2822
2823     /**
2824      * By default the frame is positioned exactly where the drag element is, so
2825      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
2826      * you do not have constraints on the obj is to have the drag frame centered
2827      * around the cursor.  Set centerFrame to true for this effect.
2828      * @property centerFrame
2829      * @type boolean
2830      */
2831     centerFrame: false,
2832
2833     /**
2834      * Creates the proxy element if it does not yet exist
2835      * @method createFrame
2836      */
2837     createFrame: function() {
2838         var self = this;
2839         var body = document.body;
2840
2841         if (!body || !body.firstChild) {
2842             setTimeout( function() { self.createFrame(); }, 50 );
2843             return;
2844         }
2845
2846         var div = this.getDragEl();
2847
2848         if (!div) {
2849             div    = document.createElement("div");
2850             div.id = this.dragElId;
2851             var s  = div.style;
2852
2853             s.position   = "absolute";
2854             s.visibility = "hidden";
2855             s.cursor     = "move";
2856             s.border     = "2px solid #aaa";
2857             s.zIndex     = 999;
2858
2859             // appendChild can blow up IE if invoked prior to the window load event
2860             // while rendering a table.  It is possible there are other scenarios
2861             // that would cause this to happen as well.
2862             body.insertBefore(div, body.firstChild);
2863         }
2864     },
2865
2866     /**
2867      * Initialization for the drag frame element.  Must be called in the
2868      * constructor of all subclasses
2869      * @method initFrame
2870      */
2871     initFrame: function() {
2872         this.createFrame();
2873     },
2874
2875     applyConfig: function() {
2876         Roo.dd.DDProxy.superclass.applyConfig.call(this);
2877
2878         this.resizeFrame = (this.config.resizeFrame !== false);
2879         this.centerFrame = (this.config.centerFrame);
2880         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2881     },
2882
2883     /**
2884      * Resizes the drag frame to the dimensions of the clicked object, positions
2885      * it over the object, and finally displays it
2886      * @method showFrame
2887      * @param {int} iPageX X click position
2888      * @param {int} iPageY Y click position
2889      * @private
2890      */
2891     showFrame: function(iPageX, iPageY) {
2892         var el = this.getEl();
2893         var dragEl = this.getDragEl();
2894         var s = dragEl.style;
2895
2896         this._resizeProxy();
2897
2898         if (this.centerFrame) {
2899             this.setDelta( Math.round(parseInt(s.width,  10)/2),
2900                            Math.round(parseInt(s.height, 10)/2) );
2901         }
2902
2903         this.setDragElPos(iPageX, iPageY);
2904
2905         Roo.fly(dragEl).show();
2906     },
2907
2908     /**
2909      * The proxy is automatically resized to the dimensions of the linked
2910      * element when a drag is initiated, unless resizeFrame is set to false
2911      * @method _resizeProxy
2912      * @private
2913      */
2914     _resizeProxy: function() {
2915         if (this.resizeFrame) {
2916             var el = this.getEl();
2917             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2918         }
2919     },
2920
2921     // overrides Roo.dd.DragDrop
2922     b4MouseDown: function(e) {
2923         var x = e.getPageX();
2924         var y = e.getPageY();
2925         this.autoOffset(x, y);
2926         this.setDragElPos(x, y);
2927     },
2928
2929     // overrides Roo.dd.DragDrop
2930     b4StartDrag: function(x, y) {
2931         // show the drag frame
2932         this.showFrame(x, y);
2933     },
2934
2935     // overrides Roo.dd.DragDrop
2936     b4EndDrag: function(e) {
2937         Roo.fly(this.getDragEl()).hide();
2938     },
2939
2940     // overrides Roo.dd.DragDrop
2941     // By default we try to move the element to the last location of the frame.
2942     // This is so that the default behavior mirrors that of Roo.dd.DD.
2943     endDrag: function(e) {
2944
2945         var lel = this.getEl();
2946         var del = this.getDragEl();
2947
2948         // Show the drag frame briefly so we can get its position
2949         del.style.visibility = "";
2950
2951         this.beforeMove();
2952         // Hide the linked element before the move to get around a Safari
2953         // rendering bug.
2954         lel.style.visibility = "hidden";
2955         Roo.dd.DDM.moveToEl(lel, del);
2956         del.style.visibility = "hidden";
2957         lel.style.visibility = "";
2958
2959         this.afterDrag();
2960     },
2961
2962     beforeMove : function(){
2963
2964     },
2965
2966     afterDrag : function(){
2967
2968     },
2969
2970     toString: function() {
2971         return ("DDProxy " + this.id);
2972     }
2973
2974 });
2975 /*
2976  * Based on:
2977  * Ext JS Library 1.1.1
2978  * Copyright(c) 2006-2007, Ext JS, LLC.
2979  *
2980  * Originally Released Under LGPL - original licence link has changed is not relivant.
2981  *
2982  * Fork - LGPL
2983  * <script type="text/javascript">
2984  */
2985
2986  /**
2987  * @class Roo.dd.DDTarget
2988  * A DragDrop implementation that does not move, but can be a drop
2989  * target.  You would get the same result by simply omitting implementation
2990  * for the event callbacks, but this way we reduce the processing cost of the
2991  * event listener and the callbacks.
2992  * @extends Roo.dd.DragDrop
2993  * @constructor
2994  * @param {String} id the id of the element that is a drop target
2995  * @param {String} sGroup the group of related DragDrop objects
2996  * @param {object} config an object containing configurable attributes
2997  *                 Valid properties for DDTarget in addition to those in
2998  *                 DragDrop:
2999  *                    none
3000  */
3001 Roo.dd.DDTarget = function(id, sGroup, config) {
3002     if (id) {
3003         this.initTarget(id, sGroup, config);
3004     }
3005     if (config.listeners || config.events) { 
3006        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
3007             listeners : config.listeners || {}, 
3008             events : config.events || {} 
3009         });    
3010     }
3011 };
3012
3013 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3014 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3015     toString: function() {
3016         return ("DDTarget " + this.id);
3017     }
3018 });
3019 /*
3020  * Based on:
3021  * Ext JS Library 1.1.1
3022  * Copyright(c) 2006-2007, Ext JS, LLC.
3023  *
3024  * Originally Released Under LGPL - original licence link has changed is not relivant.
3025  *
3026  * Fork - LGPL
3027  * <script type="text/javascript">
3028  */
3029  
3030
3031 /**
3032  * @class Roo.dd.ScrollManager
3033  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3034  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3035  * @singleton
3036  */
3037 Roo.dd.ScrollManager = function(){
3038     var ddm = Roo.dd.DragDropMgr;
3039     var els = {};
3040     var dragEl = null;
3041     var proc = {};
3042     
3043     var onStop = function(e){
3044         dragEl = null;
3045         clearProc();
3046     };
3047     
3048     var triggerRefresh = function(){
3049         if(ddm.dragCurrent){
3050              ddm.refreshCache(ddm.dragCurrent.groups);
3051         }
3052     };
3053     
3054     var doScroll = function(){
3055         if(ddm.dragCurrent){
3056             var dds = Roo.dd.ScrollManager;
3057             if(!dds.animate){
3058                 if(proc.el.scroll(proc.dir, dds.increment)){
3059                     triggerRefresh();
3060                 }
3061             }else{
3062                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3063             }
3064         }
3065     };
3066     
3067     var clearProc = function(){
3068         if(proc.id){
3069             clearInterval(proc.id);
3070         }
3071         proc.id = 0;
3072         proc.el = null;
3073         proc.dir = "";
3074     };
3075     
3076     var startProc = function(el, dir){
3077         clearProc();
3078         proc.el = el;
3079         proc.dir = dir;
3080         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3081     };
3082     
3083     var onFire = function(e, isDrop){
3084         if(isDrop || !ddm.dragCurrent){ return; }
3085         var dds = Roo.dd.ScrollManager;
3086         if(!dragEl || dragEl != ddm.dragCurrent){
3087             dragEl = ddm.dragCurrent;
3088             // refresh regions on drag start
3089             dds.refreshCache();
3090         }
3091         
3092         var xy = Roo.lib.Event.getXY(e);
3093         var pt = new Roo.lib.Point(xy[0], xy[1]);
3094         for(var id in els){
3095             var el = els[id], r = el._region;
3096             if(r && r.contains(pt) && el.isScrollable()){
3097                 if(r.bottom - pt.y <= dds.thresh){
3098                     if(proc.el != el){
3099                         startProc(el, "down");
3100                     }
3101                     return;
3102                 }else if(r.right - pt.x <= dds.thresh){
3103                     if(proc.el != el){
3104                         startProc(el, "left");
3105                     }
3106                     return;
3107                 }else if(pt.y - r.top <= dds.thresh){
3108                     if(proc.el != el){
3109                         startProc(el, "up");
3110                     }
3111                     return;
3112                 }else if(pt.x - r.left <= dds.thresh){
3113                     if(proc.el != el){
3114                         startProc(el, "right");
3115                     }
3116                     return;
3117                 }
3118             }
3119         }
3120         clearProc();
3121     };
3122     
3123     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3124     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3125     
3126     return {
3127         /**
3128          * Registers new overflow element(s) to auto scroll
3129          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3130          */
3131         register : function(el){
3132             if(el instanceof Array){
3133                 for(var i = 0, len = el.length; i < len; i++) {
3134                         this.register(el[i]);
3135                 }
3136             }else{
3137                 el = Roo.get(el);
3138                 els[el.id] = el;
3139             }
3140         },
3141         
3142         /**
3143          * Unregisters overflow element(s) so they are no longer scrolled
3144          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3145          */
3146         unregister : function(el){
3147             if(el instanceof Array){
3148                 for(var i = 0, len = el.length; i < len; i++) {
3149                         this.unregister(el[i]);
3150                 }
3151             }else{
3152                 el = Roo.get(el);
3153                 delete els[el.id];
3154             }
3155         },
3156         
3157         /**
3158          * The number of pixels from the edge of a container the pointer needs to be to 
3159          * trigger scrolling (defaults to 25)
3160          * @type Number
3161          */
3162         thresh : 25,
3163         
3164         /**
3165          * The number of pixels to scroll in each scroll increment (defaults to 50)
3166          * @type Number
3167          */
3168         increment : 100,
3169         
3170         /**
3171          * The frequency of scrolls in milliseconds (defaults to 500)
3172          * @type Number
3173          */
3174         frequency : 500,
3175         
3176         /**
3177          * True to animate the scroll (defaults to true)
3178          * @type Boolean
3179          */
3180         animate: true,
3181         
3182         /**
3183          * The animation duration in seconds - 
3184          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3185          * @type Number
3186          */
3187         animDuration: .4,
3188         
3189         /**
3190          * Manually trigger a cache refresh.
3191          */
3192         refreshCache : function(){
3193             for(var id in els){
3194                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3195                     els[id]._region = els[id].getRegion();
3196                 }
3197             }
3198         }
3199     };
3200 }();/*
3201  * Based on:
3202  * Ext JS Library 1.1.1
3203  * Copyright(c) 2006-2007, Ext JS, LLC.
3204  *
3205  * Originally Released Under LGPL - original licence link has changed is not relivant.
3206  *
3207  * Fork - LGPL
3208  * <script type="text/javascript">
3209  */
3210  
3211
3212 /**
3213  * @class Roo.dd.Registry
3214  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3215  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3216  * @singleton
3217  */
3218 Roo.dd.Registry = function(){
3219     var elements = {}; 
3220     var handles = {}; 
3221     var autoIdSeed = 0;
3222
3223     var getId = function(el, autogen){
3224         if(typeof el == "string"){
3225             return el;
3226         }
3227         var id = el.id;
3228         if(!id && autogen !== false){
3229             id = "roodd-" + (++autoIdSeed);
3230             el.id = id;
3231         }
3232         return id;
3233     };
3234     
3235     return {
3236     /**
3237      * Register a drag drop element
3238      * @param {String|HTMLElement} element The id or DOM node to register
3239      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3240      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3241      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3242      * populated in the data object (if applicable):
3243      * <pre>
3244 Value      Description<br />
3245 ---------  ------------------------------------------<br />
3246 handles    Array of DOM nodes that trigger dragging<br />
3247            for the element being registered<br />
3248 isHandle   True if the element passed in triggers<br />
3249            dragging itself, else false
3250 </pre>
3251      */
3252         register : function(el, data){
3253             data = data || {};
3254             if(typeof el == "string"){
3255                 el = document.getElementById(el);
3256             }
3257             data.ddel = el;
3258             elements[getId(el)] = data;
3259             if(data.isHandle !== false){
3260                 handles[data.ddel.id] = data;
3261             }
3262             if(data.handles){
3263                 var hs = data.handles;
3264                 for(var i = 0, len = hs.length; i < len; i++){
3265                         handles[getId(hs[i])] = data;
3266                 }
3267             }
3268         },
3269
3270     /**
3271      * Unregister a drag drop element
3272      * @param {String|HTMLElement}  element The id or DOM node to unregister
3273      */
3274         unregister : function(el){
3275             var id = getId(el, false);
3276             var data = elements[id];
3277             if(data){
3278                 delete elements[id];
3279                 if(data.handles){
3280                     var hs = data.handles;
3281                     for(var i = 0, len = hs.length; i < len; i++){
3282                         delete handles[getId(hs[i], false)];
3283                     }
3284                 }
3285             }
3286         },
3287
3288     /**
3289      * Returns the handle registered for a DOM Node by id
3290      * @param {String|HTMLElement} id The DOM node or id to look up
3291      * @return {Object} handle The custom handle data
3292      */
3293         getHandle : function(id){
3294             if(typeof id != "string"){ // must be element?
3295                 id = id.id;
3296             }
3297             return handles[id];
3298         },
3299
3300     /**
3301      * Returns the handle that is registered for the DOM node that is the target of the event
3302      * @param {Event} e The event
3303      * @return {Object} handle The custom handle data
3304      */
3305         getHandleFromEvent : function(e){
3306             var t = Roo.lib.Event.getTarget(e);
3307             return t ? handles[t.id] : null;
3308         },
3309
3310     /**
3311      * Returns a custom data object that is registered for a DOM node by id
3312      * @param {String|HTMLElement} id The DOM node or id to look up
3313      * @return {Object} data The custom data
3314      */
3315         getTarget : function(id){
3316             if(typeof id != "string"){ // must be element?
3317                 id = id.id;
3318             }
3319             return elements[id];
3320         },
3321
3322     /**
3323      * Returns a custom data object that is registered for the DOM node that is the target of the event
3324      * @param {Event} e The event
3325      * @return {Object} data The custom data
3326      */
3327         getTargetFromEvent : function(e){
3328             var t = Roo.lib.Event.getTarget(e);
3329             return t ? elements[t.id] || handles[t.id] : null;
3330         }
3331     };
3332 }();/*
3333  * Based on:
3334  * Ext JS Library 1.1.1
3335  * Copyright(c) 2006-2007, Ext JS, LLC.
3336  *
3337  * Originally Released Under LGPL - original licence link has changed is not relivant.
3338  *
3339  * Fork - LGPL
3340  * <script type="text/javascript">
3341  */
3342  
3343
3344 /**
3345  * @class Roo.dd.StatusProxy
3346  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3347  * default drag proxy used by all Roo.dd components.
3348  * @constructor
3349  * @param {Object} config
3350  */
3351 Roo.dd.StatusProxy = function(config){
3352     Roo.apply(this, config);
3353     this.id = this.id || Roo.id();
3354     this.el = new Roo.Layer({
3355         dh: {
3356             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3357                 {tag: "div", cls: "x-dd-drop-icon"},
3358                 {tag: "div", cls: "x-dd-drag-ghost"}
3359             ]
3360         }, 
3361         shadow: !config || config.shadow !== false
3362     });
3363     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3364     this.dropStatus = this.dropNotAllowed;
3365 };
3366
3367 Roo.dd.StatusProxy.prototype = {
3368     /**
3369      * @cfg {String} dropAllowed
3370      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3371      */
3372     dropAllowed : "x-dd-drop-ok",
3373     /**
3374      * @cfg {String} dropNotAllowed
3375      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3376      */
3377     dropNotAllowed : "x-dd-drop-nodrop",
3378
3379     /**
3380      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3381      * over the current target element.
3382      * @param {String} cssClass The css class for the new drop status indicator image
3383      */
3384     setStatus : function(cssClass){
3385         cssClass = cssClass || this.dropNotAllowed;
3386         if(this.dropStatus != cssClass){
3387             this.el.replaceClass(this.dropStatus, cssClass);
3388             this.dropStatus = cssClass;
3389         }
3390     },
3391
3392     /**
3393      * Resets the status indicator to the default dropNotAllowed value
3394      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3395      */
3396     reset : function(clearGhost){
3397         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3398         this.dropStatus = this.dropNotAllowed;
3399         if(clearGhost){
3400             this.ghost.update("");
3401         }
3402     },
3403
3404     /**
3405      * Updates the contents of the ghost element
3406      * @param {String} html The html that will replace the current innerHTML of the ghost element
3407      */
3408     update : function(html){
3409         if(typeof html == "string"){
3410             this.ghost.update(html);
3411         }else{
3412             this.ghost.update("");
3413             html.style.margin = "0";
3414             this.ghost.dom.appendChild(html);
3415         }
3416         // ensure float = none set?? cant remember why though.
3417         var el = this.ghost.dom.firstChild;
3418                 if(el){
3419                         Roo.fly(el).setStyle('float', 'none');
3420                 }
3421     },
3422     
3423     /**
3424      * Returns the underlying proxy {@link Roo.Layer}
3425      * @return {Roo.Layer} el
3426     */
3427     getEl : function(){
3428         return this.el;
3429     },
3430
3431     /**
3432      * Returns the ghost element
3433      * @return {Roo.Element} el
3434      */
3435     getGhost : function(){
3436         return this.ghost;
3437     },
3438
3439     /**
3440      * Hides the proxy
3441      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3442      */
3443     hide : function(clear){
3444         this.el.hide();
3445         if(clear){
3446             this.reset(true);
3447         }
3448     },
3449
3450     /**
3451      * Stops the repair animation if it's currently running
3452      */
3453     stop : function(){
3454         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3455             this.anim.stop();
3456         }
3457     },
3458
3459     /**
3460      * Displays this proxy
3461      */
3462     show : function(){
3463         this.el.show();
3464     },
3465
3466     /**
3467      * Force the Layer to sync its shadow and shim positions to the element
3468      */
3469     sync : function(){
3470         this.el.sync();
3471     },
3472
3473     /**
3474      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3475      * invalid drop operation by the item being dragged.
3476      * @param {Array} xy The XY position of the element ([x, y])
3477      * @param {Function} callback The function to call after the repair is complete
3478      * @param {Object} scope The scope in which to execute the callback
3479      */
3480     repair : function(xy, callback, scope){
3481         this.callback = callback;
3482         this.scope = scope;
3483         if(xy && this.animRepair !== false){
3484             this.el.addClass("x-dd-drag-repair");
3485             this.el.hideUnders(true);
3486             this.anim = this.el.shift({
3487                 duration: this.repairDuration || .5,
3488                 easing: 'easeOut',
3489                 xy: xy,
3490                 stopFx: true,
3491                 callback: this.afterRepair,
3492                 scope: this
3493             });
3494         }else{
3495             this.afterRepair();
3496         }
3497     },
3498
3499     // private
3500     afterRepair : function(){
3501         this.hide(true);
3502         if(typeof this.callback == "function"){
3503             this.callback.call(this.scope || this);
3504         }
3505         this.callback = null;
3506         this.scope = null;
3507     }
3508 };/*
3509  * Based on:
3510  * Ext JS Library 1.1.1
3511  * Copyright(c) 2006-2007, Ext JS, LLC.
3512  *
3513  * Originally Released Under LGPL - original licence link has changed is not relivant.
3514  *
3515  * Fork - LGPL
3516  * <script type="text/javascript">
3517  */
3518
3519 /**
3520  * @class Roo.dd.DragSource
3521  * @extends Roo.dd.DDProxy
3522  * A simple class that provides the basic implementation needed to make any element draggable.
3523  * @constructor
3524  * @param {String/HTMLElement/Element} el The container element
3525  * @param {Object} config
3526  */
3527 Roo.dd.DragSource = function(el, config){
3528     this.el = Roo.get(el);
3529     this.dragData = {};
3530     
3531     Roo.apply(this, config);
3532     
3533     if(!this.proxy){
3534         this.proxy = new Roo.dd.StatusProxy();
3535     }
3536
3537     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3538           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3539     
3540     this.dragging = false;
3541 };
3542
3543 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3544     /**
3545      * @cfg {String} dropAllowed
3546      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3547      */
3548     dropAllowed : "x-dd-drop-ok",
3549     /**
3550      * @cfg {String} dropNotAllowed
3551      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3552      */
3553     dropNotAllowed : "x-dd-drop-nodrop",
3554
3555     /**
3556      * Returns the data object associated with this drag source
3557      * @return {Object} data An object containing arbitrary data
3558      */
3559     getDragData : function(e){
3560         return this.dragData;
3561     },
3562
3563     // private
3564     onDragEnter : function(e, id){
3565         var target = Roo.dd.DragDropMgr.getDDById(id);
3566         this.cachedTarget = target;
3567         if(this.beforeDragEnter(target, e, id) !== false){
3568             if(target.isNotifyTarget){
3569                 var status = target.notifyEnter(this, e, this.dragData);
3570                 this.proxy.setStatus(status);
3571             }else{
3572                 this.proxy.setStatus(this.dropAllowed);
3573             }
3574             
3575             if(this.afterDragEnter){
3576                 /**
3577                  * An empty function by default, but provided so that you can perform a custom action
3578                  * when the dragged item enters the drop target by providing an implementation.
3579                  * @param {Roo.dd.DragDrop} target The drop target
3580                  * @param {Event} e The event object
3581                  * @param {String} id The id of the dragged element
3582                  * @method afterDragEnter
3583                  */
3584                 this.afterDragEnter(target, e, id);
3585             }
3586         }
3587     },
3588
3589     /**
3590      * An empty function by default, but provided so that you can perform a custom action
3591      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3592      * @param {Roo.dd.DragDrop} target The drop target
3593      * @param {Event} e The event object
3594      * @param {String} id The id of the dragged element
3595      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3596      */
3597     beforeDragEnter : function(target, e, id){
3598         return true;
3599     },
3600
3601     // private
3602     alignElWithMouse: function() {
3603         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3604         this.proxy.sync();
3605     },
3606
3607     // private
3608     onDragOver : function(e, id){
3609         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3610         if(this.beforeDragOver(target, e, id) !== false){
3611             if(target.isNotifyTarget){
3612                 var status = target.notifyOver(this, e, this.dragData);
3613                 this.proxy.setStatus(status);
3614             }
3615
3616             if(this.afterDragOver){
3617                 /**
3618                  * An empty function by default, but provided so that you can perform a custom action
3619                  * while the dragged item is over the drop target by providing an implementation.
3620                  * @param {Roo.dd.DragDrop} target The drop target
3621                  * @param {Event} e The event object
3622                  * @param {String} id The id of the dragged element
3623                  * @method afterDragOver
3624                  */
3625                 this.afterDragOver(target, e, id);
3626             }
3627         }
3628     },
3629
3630     /**
3631      * An empty function by default, but provided so that you can perform a custom action
3632      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3633      * @param {Roo.dd.DragDrop} target The drop target
3634      * @param {Event} e The event object
3635      * @param {String} id The id of the dragged element
3636      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3637      */
3638     beforeDragOver : function(target, e, id){
3639         return true;
3640     },
3641
3642     // private
3643     onDragOut : function(e, id){
3644         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3645         if(this.beforeDragOut(target, e, id) !== false){
3646             if(target.isNotifyTarget){
3647                 target.notifyOut(this, e, this.dragData);
3648             }
3649             this.proxy.reset();
3650             if(this.afterDragOut){
3651                 /**
3652                  * An empty function by default, but provided so that you can perform a custom action
3653                  * after the dragged item is dragged out of the target without dropping.
3654                  * @param {Roo.dd.DragDrop} target The drop target
3655                  * @param {Event} e The event object
3656                  * @param {String} id The id of the dragged element
3657                  * @method afterDragOut
3658                  */
3659                 this.afterDragOut(target, e, id);
3660             }
3661         }
3662         this.cachedTarget = null;
3663     },
3664
3665     /**
3666      * An empty function by default, but provided so that you can perform a custom action before the dragged
3667      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3668      * @param {Roo.dd.DragDrop} target The drop target
3669      * @param {Event} e The event object
3670      * @param {String} id The id of the dragged element
3671      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3672      */
3673     beforeDragOut : function(target, e, id){
3674         return true;
3675     },
3676     
3677     // private
3678     onDragDrop : function(e, id){
3679         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3680         if(this.beforeDragDrop(target, e, id) !== false){
3681             if(target.isNotifyTarget){
3682                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3683                     this.onValidDrop(target, e, id);
3684                 }else{
3685                     this.onInvalidDrop(target, e, id);
3686                 }
3687             }else{
3688                 this.onValidDrop(target, e, id);
3689             }
3690             
3691             if(this.afterDragDrop){
3692                 /**
3693                  * An empty function by default, but provided so that you can perform a custom action
3694                  * after a valid drag drop has occurred by providing an implementation.
3695                  * @param {Roo.dd.DragDrop} target The drop target
3696                  * @param {Event} e The event object
3697                  * @param {String} id The id of the dropped element
3698                  * @method afterDragDrop
3699                  */
3700                 this.afterDragDrop(target, e, id);
3701             }
3702         }
3703         delete this.cachedTarget;
3704     },
3705
3706     /**
3707      * An empty function by default, but provided so that you can perform a custom action before the dragged
3708      * item is dropped onto the target and optionally cancel the onDragDrop.
3709      * @param {Roo.dd.DragDrop} target The drop target
3710      * @param {Event} e The event object
3711      * @param {String} id The id of the dragged element
3712      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3713      */
3714     beforeDragDrop : function(target, e, id){
3715         return true;
3716     },
3717
3718     // private
3719     onValidDrop : function(target, e, id){
3720         this.hideProxy();
3721         if(this.afterValidDrop){
3722             /**
3723              * An empty function by default, but provided so that you can perform a custom action
3724              * after a valid drop has occurred by providing an implementation.
3725              * @param {Object} target The target DD 
3726              * @param {Event} e The event object
3727              * @param {String} id The id of the dropped element
3728              * @method afterInvalidDrop
3729              */
3730             this.afterValidDrop(target, e, id);
3731         }
3732     },
3733
3734     // private
3735     getRepairXY : function(e, data){
3736         return this.el.getXY();  
3737     },
3738
3739     // private
3740     onInvalidDrop : function(target, e, id){
3741         this.beforeInvalidDrop(target, e, id);
3742         if(this.cachedTarget){
3743             if(this.cachedTarget.isNotifyTarget){
3744                 this.cachedTarget.notifyOut(this, e, this.dragData);
3745             }
3746             this.cacheTarget = null;
3747         }
3748         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3749
3750         if(this.afterInvalidDrop){
3751             /**
3752              * An empty function by default, but provided so that you can perform a custom action
3753              * after an invalid drop has occurred by providing an implementation.
3754              * @param {Event} e The event object
3755              * @param {String} id The id of the dropped element
3756              * @method afterInvalidDrop
3757              */
3758             this.afterInvalidDrop(e, id);
3759         }
3760     },
3761
3762     // private
3763     afterRepair : function(){
3764         if(Roo.enableFx){
3765             this.el.highlight(this.hlColor || "c3daf9");
3766         }
3767         this.dragging = false;
3768     },
3769
3770     /**
3771      * An empty function by default, but provided so that you can perform a custom action after an invalid
3772      * drop has occurred.
3773      * @param {Roo.dd.DragDrop} target The drop target
3774      * @param {Event} e The event object
3775      * @param {String} id The id of the dragged element
3776      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3777      */
3778     beforeInvalidDrop : function(target, e, id){
3779         return true;
3780     },
3781
3782     // private
3783     handleMouseDown : function(e){
3784         if(this.dragging) {
3785             return;
3786         }
3787         var data = this.getDragData(e);
3788         if(data && this.onBeforeDrag(data, e) !== false){
3789             this.dragData = data;
3790             this.proxy.stop();
3791             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3792         } 
3793     },
3794
3795     /**
3796      * An empty function by default, but provided so that you can perform a custom action before the initial
3797      * drag event begins and optionally cancel it.
3798      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3799      * @param {Event} e The event object
3800      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3801      */
3802     onBeforeDrag : function(data, e){
3803         return true;
3804     },
3805
3806     /**
3807      * An empty function by default, but provided so that you can perform a custom action once the initial
3808      * drag event has begun.  The drag cannot be canceled from this function.
3809      * @param {Number} x The x position of the click on the dragged object
3810      * @param {Number} y The y position of the click on the dragged object
3811      */
3812     onStartDrag : Roo.emptyFn,
3813
3814     // private - YUI override
3815     startDrag : function(x, y){
3816         this.proxy.reset();
3817         this.dragging = true;
3818         this.proxy.update("");
3819         this.onInitDrag(x, y);
3820         this.proxy.show();
3821     },
3822
3823     // private
3824     onInitDrag : function(x, y){
3825         var clone = this.el.dom.cloneNode(true);
3826         clone.id = Roo.id(); // prevent duplicate ids
3827         this.proxy.update(clone);
3828         this.onStartDrag(x, y);
3829         return true;
3830     },
3831
3832     /**
3833      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3834      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3835      */
3836     getProxy : function(){
3837         return this.proxy;  
3838     },
3839
3840     /**
3841      * Hides the drag source's {@link Roo.dd.StatusProxy}
3842      */
3843     hideProxy : function(){
3844         this.proxy.hide();  
3845         this.proxy.reset(true);
3846         this.dragging = false;
3847     },
3848
3849     // private
3850     triggerCacheRefresh : function(){
3851         Roo.dd.DDM.refreshCache(this.groups);
3852     },
3853
3854     // private - override to prevent hiding
3855     b4EndDrag: function(e) {
3856     },
3857
3858     // private - override to prevent moving
3859     endDrag : function(e){
3860         this.onEndDrag(this.dragData, e);
3861     },
3862
3863     // private
3864     onEndDrag : function(data, e){
3865     },
3866     
3867     // private - pin to cursor
3868     autoOffset : function(x, y) {
3869         this.setDelta(-12, -20);
3870     }    
3871 });/*
3872  * Based on:
3873  * Ext JS Library 1.1.1
3874  * Copyright(c) 2006-2007, Ext JS, LLC.
3875  *
3876  * Originally Released Under LGPL - original licence link has changed is not relivant.
3877  *
3878  * Fork - LGPL
3879  * <script type="text/javascript">
3880  */
3881
3882
3883 /**
3884  * @class Roo.dd.DropTarget
3885  * @extends Roo.dd.DDTarget
3886  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3887  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3888  * @constructor
3889  * @param {String/HTMLElement/Element} el The container element
3890  * @param {Object} config
3891  */
3892 Roo.dd.DropTarget = function(el, config){
3893     this.el = Roo.get(el);
3894     
3895     var listeners = false; ;
3896     if (config && config.listeners) {
3897         listeners= config.listeners;
3898         delete config.listeners;
3899     }
3900     Roo.apply(this, config);
3901     
3902     if(this.containerScroll){
3903         Roo.dd.ScrollManager.register(this.el);
3904     }
3905     this.addEvents( {
3906          /**
3907          * @scope Roo.dd.DropTarget
3908          */
3909          
3910          /**
3911          * @event enter
3912          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3913          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3914          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3915          * 
3916          * IMPORTANT : it should set this.overClass and this.dropAllowed
3917          * 
3918          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3919          * @param {Event} e The event
3920          * @param {Object} data An object containing arbitrary data supplied by the drag source
3921          */
3922         "enter" : true,
3923         
3924          /**
3925          * @event over
3926          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3927          * This method will be called on every mouse movement while the drag source is over the drop target.
3928          * This default implementation simply returns the dropAllowed config value.
3929          * 
3930          * IMPORTANT : it should set this.dropAllowed
3931          * 
3932          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3933          * @param {Event} e The event
3934          * @param {Object} data An object containing arbitrary data supplied by the drag source
3935          
3936          */
3937         "over" : true,
3938         /**
3939          * @event out
3940          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3941          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3942          * overClass (if any) from the drop element.
3943          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3944          * @param {Event} e The event
3945          * @param {Object} data An object containing arbitrary data supplied by the drag source
3946          */
3947          "out" : true,
3948          
3949         /**
3950          * @event drop
3951          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3952          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3953          * implementation that does something to process the drop event and returns true so that the drag source's
3954          * repair action does not run.
3955          * 
3956          * IMPORTANT : it should set this.success
3957          * 
3958          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3959          * @param {Event} e The event
3960          * @param {Object} data An object containing arbitrary data supplied by the drag source
3961         */
3962          "drop" : true
3963     });
3964             
3965      
3966     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3967         this.el.dom, 
3968         this.ddGroup || this.group,
3969         {
3970             isTarget: true,
3971             listeners : listeners || {} 
3972            
3973         
3974         }
3975     );
3976
3977 };
3978
3979 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
3980     /**
3981      * @cfg {String} overClass
3982      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
3983      */
3984      /**
3985      * @cfg {String} ddGroup
3986      * The drag drop group to handle drop events for
3987      */
3988      
3989     /**
3990      * @cfg {String} dropAllowed
3991      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3992      */
3993     dropAllowed : "x-dd-drop-ok",
3994     /**
3995      * @cfg {String} dropNotAllowed
3996      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3997      */
3998     dropNotAllowed : "x-dd-drop-nodrop",
3999     /**
4000      * @cfg {boolean} success
4001      * set this after drop listener.. 
4002      */
4003     success : false,
4004     /**
4005      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
4006      * if the drop point is valid for over/enter..
4007      */
4008     valid : false,
4009     // private
4010     isTarget : true,
4011
4012     // private
4013     isNotifyTarget : true,
4014     
4015     /**
4016      * @hide
4017      */
4018     notifyEnter : function(dd, e, data)
4019     {
4020         this.valid = true;
4021         this.fireEvent('enter', dd, e, data);
4022         if(this.overClass){
4023             this.el.addClass(this.overClass);
4024         }
4025         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4026             this.valid ? this.dropAllowed : this.dropNotAllowed
4027         );
4028     },
4029
4030     /**
4031      * @hide
4032      */
4033     notifyOver : function(dd, e, data)
4034     {
4035         this.valid = true;
4036         this.fireEvent('over', dd, e, data);
4037         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4038             this.valid ? this.dropAllowed : this.dropNotAllowed
4039         );
4040     },
4041
4042     /**
4043      * @hide
4044      */
4045     notifyOut : function(dd, e, data)
4046     {
4047         this.fireEvent('out', dd, e, data);
4048         if(this.overClass){
4049             this.el.removeClass(this.overClass);
4050         }
4051     },
4052
4053     /**
4054      * @hide
4055      */
4056     notifyDrop : function(dd, e, data)
4057     {
4058         this.success = false;
4059         this.fireEvent('drop', dd, e, data);
4060         return this.success;
4061     }
4062 });/*
4063  * Based on:
4064  * Ext JS Library 1.1.1
4065  * Copyright(c) 2006-2007, Ext JS, LLC.
4066  *
4067  * Originally Released Under LGPL - original licence link has changed is not relivant.
4068  *
4069  * Fork - LGPL
4070  * <script type="text/javascript">
4071  */
4072
4073
4074 /**
4075  * @class Roo.dd.DragZone
4076  * @extends Roo.dd.DragSource
4077  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4078  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4079  * @constructor
4080  * @param {String/HTMLElement/Element} el The container element
4081  * @param {Object} config
4082  */
4083 Roo.dd.DragZone = function(el, config){
4084     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4085     if(this.containerScroll){
4086         Roo.dd.ScrollManager.register(this.el);
4087     }
4088 };
4089
4090 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4091     /**
4092      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4093      * for auto scrolling during drag operations.
4094      */
4095     /**
4096      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4097      * method after a failed drop (defaults to "c3daf9" - light blue)
4098      */
4099
4100     /**
4101      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4102      * for a valid target to drag based on the mouse down. Override this method
4103      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4104      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4105      * @param {EventObject} e The mouse down event
4106      * @return {Object} The dragData
4107      */
4108     getDragData : function(e){
4109         return Roo.dd.Registry.getHandleFromEvent(e);
4110     },
4111     
4112     /**
4113      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4114      * this.dragData.ddel
4115      * @param {Number} x The x position of the click on the dragged object
4116      * @param {Number} y The y position of the click on the dragged object
4117      * @return {Boolean} true to continue the drag, false to cancel
4118      */
4119     onInitDrag : function(x, y){
4120         this.proxy.update(this.dragData.ddel.cloneNode(true));
4121         this.onStartDrag(x, y);
4122         return true;
4123     },
4124     
4125     /**
4126      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4127      */
4128     afterRepair : function(){
4129         if(Roo.enableFx){
4130             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4131         }
4132         this.dragging = false;
4133     },
4134
4135     /**
4136      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4137      * the XY of this.dragData.ddel
4138      * @param {EventObject} e The mouse up event
4139      * @return {Array} The xy location (e.g. [100, 200])
4140      */
4141     getRepairXY : function(e){
4142         return Roo.Element.fly(this.dragData.ddel).getXY();  
4143     }
4144 });/*
4145  * Based on:
4146  * Ext JS Library 1.1.1
4147  * Copyright(c) 2006-2007, Ext JS, LLC.
4148  *
4149  * Originally Released Under LGPL - original licence link has changed is not relivant.
4150  *
4151  * Fork - LGPL
4152  * <script type="text/javascript">
4153  */
4154 /**
4155  * @class Roo.dd.DropZone
4156  * @extends Roo.dd.DropTarget
4157  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4158  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4159  * @constructor
4160  * @param {String/HTMLElement/Element} el The container element
4161  * @param {Object} config
4162  */
4163 Roo.dd.DropZone = function(el, config){
4164     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4165 };
4166
4167 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4168     /**
4169      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4170      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4171      * provide your own custom lookup.
4172      * @param {Event} e The event
4173      * @return {Object} data The custom data
4174      */
4175     getTargetFromEvent : function(e){
4176         return Roo.dd.Registry.getTargetFromEvent(e);
4177     },
4178
4179     /**
4180      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4181      * that it has registered.  This method has no default implementation and should be overridden to provide
4182      * node-specific processing if necessary.
4183      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4184      * {@link #getTargetFromEvent} for this node)
4185      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4186      * @param {Event} e The event
4187      * @param {Object} data An object containing arbitrary data supplied by the drag source
4188      */
4189     onNodeEnter : function(n, dd, e, data){
4190         
4191     },
4192
4193     /**
4194      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4195      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4196      * overridden to provide the proper feedback.
4197      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4198      * {@link #getTargetFromEvent} for this node)
4199      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4200      * @param {Event} e The event
4201      * @param {Object} data An object containing arbitrary data supplied by the drag source
4202      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4203      * underlying {@link Roo.dd.StatusProxy} can be updated
4204      */
4205     onNodeOver : function(n, dd, e, data){
4206         return this.dropAllowed;
4207     },
4208
4209     /**
4210      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4211      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4212      * node-specific processing if necessary.
4213      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4214      * {@link #getTargetFromEvent} for this node)
4215      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4216      * @param {Event} e The event
4217      * @param {Object} data An object containing arbitrary data supplied by the drag source
4218      */
4219     onNodeOut : function(n, dd, e, data){
4220         
4221     },
4222
4223     /**
4224      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4225      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4226      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4227      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4228      * {@link #getTargetFromEvent} for this node)
4229      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4230      * @param {Event} e The event
4231      * @param {Object} data An object containing arbitrary data supplied by the drag source
4232      * @return {Boolean} True if the drop was valid, else false
4233      */
4234     onNodeDrop : function(n, dd, e, data){
4235         return false;
4236     },
4237
4238     /**
4239      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4240      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4241      * it should be overridden to provide the proper feedback if necessary.
4242      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4243      * @param {Event} e The event
4244      * @param {Object} data An object containing arbitrary data supplied by the drag source
4245      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4246      * underlying {@link Roo.dd.StatusProxy} can be updated
4247      */
4248     onContainerOver : function(dd, e, data){
4249         return this.dropNotAllowed;
4250     },
4251
4252     /**
4253      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4254      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4255      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4256      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4257      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4258      * @param {Event} e The event
4259      * @param {Object} data An object containing arbitrary data supplied by the drag source
4260      * @return {Boolean} True if the drop was valid, else false
4261      */
4262     onContainerDrop : function(dd, e, data){
4263         return false;
4264     },
4265
4266     /**
4267      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4268      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4269      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4270      * you should override this method and provide a custom implementation.
4271      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4272      * @param {Event} e The event
4273      * @param {Object} data An object containing arbitrary data supplied by the drag source
4274      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4275      * underlying {@link Roo.dd.StatusProxy} can be updated
4276      */
4277     notifyEnter : function(dd, e, data){
4278         return this.dropNotAllowed;
4279     },
4280
4281     /**
4282      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4283      * This method will be called on every mouse movement while the drag source is over the drop zone.
4284      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4285      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4286      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4287      * registered node, it will call {@link #onContainerOver}.
4288      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4289      * @param {Event} e The event
4290      * @param {Object} data An object containing arbitrary data supplied by the drag source
4291      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4292      * underlying {@link Roo.dd.StatusProxy} can be updated
4293      */
4294     notifyOver : function(dd, e, data){
4295         var n = this.getTargetFromEvent(e);
4296         if(!n){ // not over valid drop target
4297             if(this.lastOverNode){
4298                 this.onNodeOut(this.lastOverNode, dd, e, data);
4299                 this.lastOverNode = null;
4300             }
4301             return this.onContainerOver(dd, e, data);
4302         }
4303         if(this.lastOverNode != n){
4304             if(this.lastOverNode){
4305                 this.onNodeOut(this.lastOverNode, dd, e, data);
4306             }
4307             this.onNodeEnter(n, dd, e, data);
4308             this.lastOverNode = n;
4309         }
4310         return this.onNodeOver(n, dd, e, data);
4311     },
4312
4313     /**
4314      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4315      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4316      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4317      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4318      * @param {Event} e The event
4319      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4320      */
4321     notifyOut : function(dd, e, data){
4322         if(this.lastOverNode){
4323             this.onNodeOut(this.lastOverNode, dd, e, data);
4324             this.lastOverNode = null;
4325         }
4326     },
4327
4328     /**
4329      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4330      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4331      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4332      * otherwise it will call {@link #onContainerDrop}.
4333      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4334      * @param {Event} e The event
4335      * @param {Object} data An object containing arbitrary data supplied by the drag source
4336      * @return {Boolean} True if the drop was valid, else false
4337      */
4338     notifyDrop : function(dd, e, data){
4339         if(this.lastOverNode){
4340             this.onNodeOut(this.lastOverNode, dd, e, data);
4341             this.lastOverNode = null;
4342         }
4343         var n = this.getTargetFromEvent(e);
4344         return n ?
4345             this.onNodeDrop(n, dd, e, data) :
4346             this.onContainerDrop(dd, e, data);
4347     },
4348
4349     // private
4350     triggerCacheRefresh : function(){
4351         Roo.dd.DDM.refreshCache(this.groups);
4352     }  
4353 });/*
4354  * Based on:
4355  * Ext JS Library 1.1.1
4356  * Copyright(c) 2006-2007, Ext JS, LLC.
4357  *
4358  * Originally Released Under LGPL - original licence link has changed is not relivant.
4359  *
4360  * Fork - LGPL
4361  * <script type="text/javascript">
4362  */
4363
4364
4365 /**
4366  * @class Roo.data.SortTypes
4367  * @singleton
4368  * Defines the default sorting (casting?) comparison functions used when sorting data.
4369  */
4370 Roo.data.SortTypes = {
4371     /**
4372      * Default sort that does nothing
4373      * @param {Mixed} s The value being converted
4374      * @return {Mixed} The comparison value
4375      */
4376     none : function(s){
4377         return s;
4378     },
4379     
4380     /**
4381      * The regular expression used to strip tags
4382      * @type {RegExp}
4383      * @property
4384      */
4385     stripTagsRE : /<\/?[^>]+>/gi,
4386     
4387     /**
4388      * Strips all HTML tags to sort on text only
4389      * @param {Mixed} s The value being converted
4390      * @return {String} The comparison value
4391      */
4392     asText : function(s){
4393         return String(s).replace(this.stripTagsRE, "");
4394     },
4395     
4396     /**
4397      * Strips all HTML tags to sort on text only - Case insensitive
4398      * @param {Mixed} s The value being converted
4399      * @return {String} The comparison value
4400      */
4401     asUCText : function(s){
4402         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4403     },
4404     
4405     /**
4406      * Case insensitive string
4407      * @param {Mixed} s The value being converted
4408      * @return {String} The comparison value
4409      */
4410     asUCString : function(s) {
4411         return String(s).toUpperCase();
4412     },
4413     
4414     /**
4415      * Date sorting
4416      * @param {Mixed} s The value being converted
4417      * @return {Number} The comparison value
4418      */
4419     asDate : function(s) {
4420         if(!s){
4421             return 0;
4422         }
4423         if(s instanceof Date){
4424             return s.getTime();
4425         }
4426         return Date.parse(String(s));
4427     },
4428     
4429     /**
4430      * Float sorting
4431      * @param {Mixed} s The value being converted
4432      * @return {Float} The comparison value
4433      */
4434     asFloat : function(s) {
4435         var val = parseFloat(String(s).replace(/,/g, ""));
4436         if(isNaN(val)) val = 0;
4437         return val;
4438     },
4439     
4440     /**
4441      * Integer sorting
4442      * @param {Mixed} s The value being converted
4443      * @return {Number} The comparison value
4444      */
4445     asInt : function(s) {
4446         var val = parseInt(String(s).replace(/,/g, ""));
4447         if(isNaN(val)) val = 0;
4448         return val;
4449     }
4450 };/*
4451  * Based on:
4452  * Ext JS Library 1.1.1
4453  * Copyright(c) 2006-2007, Ext JS, LLC.
4454  *
4455  * Originally Released Under LGPL - original licence link has changed is not relivant.
4456  *
4457  * Fork - LGPL
4458  * <script type="text/javascript">
4459  */
4460
4461 /**
4462 * @class Roo.data.Record
4463  * Instances of this class encapsulate both record <em>definition</em> information, and record
4464  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4465  * to access Records cached in an {@link Roo.data.Store} object.<br>
4466  * <p>
4467  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4468  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4469  * objects.<br>
4470  * <p>
4471  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4472  * @constructor
4473  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4474  * {@link #create}. The parameters are the same.
4475  * @param {Array} data An associative Array of data values keyed by the field name.
4476  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4477  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4478  * not specified an integer id is generated.
4479  */
4480 Roo.data.Record = function(data, id){
4481     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4482     this.data = data;
4483 };
4484
4485 /**
4486  * Generate a constructor for a specific record layout.
4487  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4488  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4489  * Each field definition object may contain the following properties: <ul>
4490  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
4491  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4492  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4493  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4494  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4495  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4496  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4497  * this may be omitted.</p></li>
4498  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4499  * <ul><li>auto (Default, implies no conversion)</li>
4500  * <li>string</li>
4501  * <li>int</li>
4502  * <li>float</li>
4503  * <li>boolean</li>
4504  * <li>date</li></ul></p></li>
4505  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4506  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4507  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4508  * by the Reader into an object that will be stored in the Record. It is passed the
4509  * following parameters:<ul>
4510  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4511  * </ul></p></li>
4512  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4513  * </ul>
4514  * <br>usage:<br><pre><code>
4515 var TopicRecord = Roo.data.Record.create(
4516     {name: 'title', mapping: 'topic_title'},
4517     {name: 'author', mapping: 'username'},
4518     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4519     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4520     {name: 'lastPoster', mapping: 'user2'},
4521     {name: 'excerpt', mapping: 'post_text'}
4522 );
4523
4524 var myNewRecord = new TopicRecord({
4525     title: 'Do my job please',
4526     author: 'noobie',
4527     totalPosts: 1,
4528     lastPost: new Date(),
4529     lastPoster: 'Animal',
4530     excerpt: 'No way dude!'
4531 });
4532 myStore.add(myNewRecord);
4533 </code></pre>
4534  * @method create
4535  * @static
4536  */
4537 Roo.data.Record.create = function(o){
4538     var f = function(){
4539         f.superclass.constructor.apply(this, arguments);
4540     };
4541     Roo.extend(f, Roo.data.Record);
4542     var p = f.prototype;
4543     p.fields = new Roo.util.MixedCollection(false, function(field){
4544         return field.name;
4545     });
4546     for(var i = 0, len = o.length; i < len; i++){
4547         p.fields.add(new Roo.data.Field(o[i]));
4548     }
4549     f.getField = function(name){
4550         return p.fields.get(name);  
4551     };
4552     return f;
4553 };
4554
4555 Roo.data.Record.AUTO_ID = 1000;
4556 Roo.data.Record.EDIT = 'edit';
4557 Roo.data.Record.REJECT = 'reject';
4558 Roo.data.Record.COMMIT = 'commit';
4559
4560 Roo.data.Record.prototype = {
4561     /**
4562      * Readonly flag - true if this record has been modified.
4563      * @type Boolean
4564      */
4565     dirty : false,
4566     editing : false,
4567     error: null,
4568     modified: null,
4569
4570     // private
4571     join : function(store){
4572         this.store = store;
4573     },
4574
4575     /**
4576      * Set the named field to the specified value.
4577      * @param {String} name The name of the field to set.
4578      * @param {Object} value The value to set the field to.
4579      */
4580     set : function(name, value){
4581         if(this.data[name] == value){
4582             return;
4583         }
4584         this.dirty = true;
4585         if(!this.modified){
4586             this.modified = {};
4587         }
4588         if(typeof this.modified[name] == 'undefined'){
4589             this.modified[name] = this.data[name];
4590         }
4591         this.data[name] = value;
4592         if(!this.editing && this.store){
4593             this.store.afterEdit(this);
4594         }       
4595     },
4596
4597     /**
4598      * Get the value of the named field.
4599      * @param {String} name The name of the field to get the value of.
4600      * @return {Object} The value of the field.
4601      */
4602     get : function(name){
4603         return this.data[name]; 
4604     },
4605
4606     // private
4607     beginEdit : function(){
4608         this.editing = true;
4609         this.modified = {}; 
4610     },
4611
4612     // private
4613     cancelEdit : function(){
4614         this.editing = false;
4615         delete this.modified;
4616     },
4617
4618     // private
4619     endEdit : function(){
4620         this.editing = false;
4621         if(this.dirty && this.store){
4622             this.store.afterEdit(this);
4623         }
4624     },
4625
4626     /**
4627      * Usually called by the {@link Roo.data.Store} which owns the Record.
4628      * Rejects all changes made to the Record since either creation, or the last commit operation.
4629      * Modified fields are reverted to their original values.
4630      * <p>
4631      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4632      * of reject operations.
4633      */
4634     reject : function(){
4635         var m = this.modified;
4636         for(var n in m){
4637             if(typeof m[n] != "function"){
4638                 this.data[n] = m[n];
4639             }
4640         }
4641         this.dirty = false;
4642         delete this.modified;
4643         this.editing = false;
4644         if(this.store){
4645             this.store.afterReject(this);
4646         }
4647     },
4648
4649     /**
4650      * Usually called by the {@link Roo.data.Store} which owns the Record.
4651      * Commits all changes made to the Record since either creation, or the last commit operation.
4652      * <p>
4653      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4654      * of commit operations.
4655      */
4656     commit : function(){
4657         this.dirty = false;
4658         delete this.modified;
4659         this.editing = false;
4660         if(this.store){
4661             this.store.afterCommit(this);
4662         }
4663     },
4664
4665     // private
4666     hasError : function(){
4667         return this.error != null;
4668     },
4669
4670     // private
4671     clearError : function(){
4672         this.error = null;
4673     },
4674
4675     /**
4676      * Creates a copy of this record.
4677      * @param {String} id (optional) A new record id if you don't want to use this record's id
4678      * @return {Record}
4679      */
4680     copy : function(newId) {
4681         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4682     }
4683 };/*
4684  * Based on:
4685  * Ext JS Library 1.1.1
4686  * Copyright(c) 2006-2007, Ext JS, LLC.
4687  *
4688  * Originally Released Under LGPL - original licence link has changed is not relivant.
4689  *
4690  * Fork - LGPL
4691  * <script type="text/javascript">
4692  */
4693
4694
4695
4696 /**
4697  * @class Roo.data.Store
4698  * @extends Roo.util.Observable
4699  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4700  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4701  * <p>
4702  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
4703  * has no knowledge of the format of the data returned by the Proxy.<br>
4704  * <p>
4705  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4706  * instances from the data object. These records are cached and made available through accessor functions.
4707  * @constructor
4708  * Creates a new Store.
4709  * @param {Object} config A config object containing the objects needed for the Store to access data,
4710  * and read the data into Records.
4711  */
4712 Roo.data.Store = function(config){
4713     this.data = new Roo.util.MixedCollection(false);
4714     this.data.getKey = function(o){
4715         return o.id;
4716     };
4717     this.baseParams = {};
4718     // private
4719     this.paramNames = {
4720         "start" : "start",
4721         "limit" : "limit",
4722         "sort" : "sort",
4723         "dir" : "dir",
4724         "multisort" : "_multisort"
4725     };
4726
4727     if(config && config.data){
4728         this.inlineData = config.data;
4729         delete config.data;
4730     }
4731
4732     Roo.apply(this, config);
4733     
4734     if(this.reader){ // reader passed
4735         this.reader = Roo.factory(this.reader, Roo.data);
4736         this.reader.xmodule = this.xmodule || false;
4737         if(!this.recordType){
4738             this.recordType = this.reader.recordType;
4739         }
4740         if(this.reader.onMetaChange){
4741             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4742         }
4743     }
4744
4745     if(this.recordType){
4746         this.fields = this.recordType.prototype.fields;
4747     }
4748     this.modified = [];
4749
4750     this.addEvents({
4751         /**
4752          * @event datachanged
4753          * Fires when the data cache has changed, and a widget which is using this Store
4754          * as a Record cache should refresh its view.
4755          * @param {Store} this
4756          */
4757         datachanged : true,
4758         /**
4759          * @event metachange
4760          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4761          * @param {Store} this
4762          * @param {Object} meta The JSON metadata
4763          */
4764         metachange : true,
4765         /**
4766          * @event add
4767          * Fires when Records have been added to the Store
4768          * @param {Store} this
4769          * @param {Roo.data.Record[]} records The array of Records added
4770          * @param {Number} index The index at which the record(s) were added
4771          */
4772         add : true,
4773         /**
4774          * @event remove
4775          * Fires when a Record has been removed from the Store
4776          * @param {Store} this
4777          * @param {Roo.data.Record} record The Record that was removed
4778          * @param {Number} index The index at which the record was removed
4779          */
4780         remove : true,
4781         /**
4782          * @event update
4783          * Fires when a Record has been updated
4784          * @param {Store} this
4785          * @param {Roo.data.Record} record The Record that was updated
4786          * @param {String} operation The update operation being performed.  Value may be one of:
4787          * <pre><code>
4788  Roo.data.Record.EDIT
4789  Roo.data.Record.REJECT
4790  Roo.data.Record.COMMIT
4791          * </code></pre>
4792          */
4793         update : true,
4794         /**
4795          * @event clear
4796          * Fires when the data cache has been cleared.
4797          * @param {Store} this
4798          */
4799         clear : true,
4800         /**
4801          * @event beforeload
4802          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4803          * the load action will be canceled.
4804          * @param {Store} this
4805          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4806          */
4807         beforeload : true,
4808         /**
4809          * @event load
4810          * Fires after a new set of Records has been loaded.
4811          * @param {Store} this
4812          * @param {Roo.data.Record[]} records The Records that were loaded
4813          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4814          */
4815         load : true,
4816         /**
4817          * @event loadexception
4818          * Fires if an exception occurs in the Proxy during loading.
4819          * Called with the signature of the Proxy's "loadexception" event.
4820          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4821          * 
4822          * @param {Proxy} 
4823          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4824          * @param {Object} load options 
4825          * @param {Object} jsonData from your request (normally this contains the Exception)
4826          */
4827         loadexception : true
4828     });
4829     
4830     if(this.proxy){
4831         this.proxy = Roo.factory(this.proxy, Roo.data);
4832         this.proxy.xmodule = this.xmodule || false;
4833         this.relayEvents(this.proxy,  ["loadexception"]);
4834     }
4835     this.sortToggle = {};
4836     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
4837
4838     Roo.data.Store.superclass.constructor.call(this);
4839
4840     if(this.inlineData){
4841         this.loadData(this.inlineData);
4842         delete this.inlineData;
4843     }
4844 };
4845 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4846      /**
4847     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4848     * without a remote query - used by combo/forms at present.
4849     */
4850     
4851     /**
4852     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4853     */
4854     /**
4855     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4856     */
4857     /**
4858     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4859     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4860     */
4861     /**
4862     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4863     * on any HTTP request
4864     */
4865     /**
4866     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4867     */
4868     /**
4869     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
4870     */
4871     multiSort: false,
4872     /**
4873     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4874     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4875     */
4876     remoteSort : false,
4877
4878     /**
4879     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4880      * loaded or when a record is removed. (defaults to false).
4881     */
4882     pruneModifiedRecords : false,
4883
4884     // private
4885     lastOptions : null,
4886
4887     /**
4888      * Add Records to the Store and fires the add event.
4889      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4890      */
4891     add : function(records){
4892         records = [].concat(records);
4893         for(var i = 0, len = records.length; i < len; i++){
4894             records[i].join(this);
4895         }
4896         var index = this.data.length;
4897         this.data.addAll(records);
4898         this.fireEvent("add", this, records, index);
4899     },
4900
4901     /**
4902      * Remove a Record from the Store and fires the remove event.
4903      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4904      */
4905     remove : function(record){
4906         var index = this.data.indexOf(record);
4907         this.data.removeAt(index);
4908         if(this.pruneModifiedRecords){
4909             this.modified.remove(record);
4910         }
4911         this.fireEvent("remove", this, record, index);
4912     },
4913
4914     /**
4915      * Remove all Records from the Store and fires the clear event.
4916      */
4917     removeAll : function(){
4918         this.data.clear();
4919         if(this.pruneModifiedRecords){
4920             this.modified = [];
4921         }
4922         this.fireEvent("clear", this);
4923     },
4924
4925     /**
4926      * Inserts Records to the Store at the given index and fires the add event.
4927      * @param {Number} index The start index at which to insert the passed Records.
4928      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4929      */
4930     insert : function(index, records){
4931         records = [].concat(records);
4932         for(var i = 0, len = records.length; i < len; i++){
4933             this.data.insert(index, records[i]);
4934             records[i].join(this);
4935         }
4936         this.fireEvent("add", this, records, index);
4937     },
4938
4939     /**
4940      * Get the index within the cache of the passed Record.
4941      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4942      * @return {Number} The index of the passed Record. Returns -1 if not found.
4943      */
4944     indexOf : function(record){
4945         return this.data.indexOf(record);
4946     },
4947
4948     /**
4949      * Get the index within the cache of the Record with the passed id.
4950      * @param {String} id The id of the Record to find.
4951      * @return {Number} The index of the Record. Returns -1 if not found.
4952      */
4953     indexOfId : function(id){
4954         return this.data.indexOfKey(id);
4955     },
4956
4957     /**
4958      * Get the Record with the specified id.
4959      * @param {String} id The id of the Record to find.
4960      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4961      */
4962     getById : function(id){
4963         return this.data.key(id);
4964     },
4965
4966     /**
4967      * Get the Record at the specified index.
4968      * @param {Number} index The index of the Record to find.
4969      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
4970      */
4971     getAt : function(index){
4972         return this.data.itemAt(index);
4973     },
4974
4975     /**
4976      * Returns a range of Records between specified indices.
4977      * @param {Number} startIndex (optional) The starting index (defaults to 0)
4978      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
4979      * @return {Roo.data.Record[]} An array of Records
4980      */
4981     getRange : function(start, end){
4982         return this.data.getRange(start, end);
4983     },
4984
4985     // private
4986     storeOptions : function(o){
4987         o = Roo.apply({}, o);
4988         delete o.callback;
4989         delete o.scope;
4990         this.lastOptions = o;
4991     },
4992
4993     /**
4994      * Loads the Record cache from the configured Proxy using the configured Reader.
4995      * <p>
4996      * If using remote paging, then the first load call must specify the <em>start</em>
4997      * and <em>limit</em> properties in the options.params property to establish the initial
4998      * position within the dataset, and the number of Records to cache on each read from the Proxy.
4999      * <p>
5000      * <strong>It is important to note that for remote data sources, loading is asynchronous,
5001      * and this call will return before the new data has been loaded. Perform any post-processing
5002      * in a callback function, or in a "load" event handler.</strong>
5003      * <p>
5004      * @param {Object} options An object containing properties which control loading options:<ul>
5005      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5006      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5007      * passed the following arguments:<ul>
5008      * <li>r : Roo.data.Record[]</li>
5009      * <li>options: Options object from the load call</li>
5010      * <li>success: Boolean success indicator</li></ul></li>
5011      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5012      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5013      * </ul>
5014      */
5015     load : function(options){
5016         options = options || {};
5017         if(this.fireEvent("beforeload", this, options) !== false){
5018             this.storeOptions(options);
5019             var p = Roo.apply(options.params || {}, this.baseParams);
5020             // if meta was not loaded from remote source.. try requesting it.
5021             if (!this.reader.metaFromRemote) {
5022                 p._requestMeta = 1;
5023             }
5024             if(this.sortInfo && this.remoteSort){
5025                 var pn = this.paramNames;
5026                 p[pn["sort"]] = this.sortInfo.field;
5027                 p[pn["dir"]] = this.sortInfo.direction;
5028             }
5029             if (this.multiSort) {
5030                 var pn = this.paramNames;
5031                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
5032             }
5033             
5034             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5035         }
5036     },
5037
5038     /**
5039      * Reloads the Record cache from the configured Proxy using the configured Reader and
5040      * the options from the last load operation performed.
5041      * @param {Object} options (optional) An object containing properties which may override the options
5042      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5043      * the most recently used options are reused).
5044      */
5045     reload : function(options){
5046         this.load(Roo.applyIf(options||{}, this.lastOptions));
5047     },
5048
5049     // private
5050     // Called as a callback by the Reader during a load operation.
5051     loadRecords : function(o, options, success){
5052         if(!o || success === false){
5053             if(success !== false){
5054                 this.fireEvent("load", this, [], options);
5055             }
5056             if(options.callback){
5057                 options.callback.call(options.scope || this, [], options, false);
5058             }
5059             return;
5060         }
5061         // if data returned failure - throw an exception.
5062         if (o.success === false) {
5063             // show a message if no listener is registered.
5064             if (!this.hasListener('loadexception') && typeof(this.reader.jsonData.errorMsg) != 'undefined') {
5065                     Roo.MessageBox.alert("Error loading",this.reader.jsonData.errorMsg);
5066             }
5067             // loadmask wil be hooked into this..
5068             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
5069             return;
5070         }
5071         var r = o.records, t = o.totalRecords || r.length;
5072         if(!options || options.add !== true){
5073             if(this.pruneModifiedRecords){
5074                 this.modified = [];
5075             }
5076             for(var i = 0, len = r.length; i < len; i++){
5077                 r[i].join(this);
5078             }
5079             if(this.snapshot){
5080                 this.data = this.snapshot;
5081                 delete this.snapshot;
5082             }
5083             this.data.clear();
5084             this.data.addAll(r);
5085             this.totalLength = t;
5086             this.applySort();
5087             this.fireEvent("datachanged", this);
5088         }else{
5089             this.totalLength = Math.max(t, this.data.length+r.length);
5090             this.add(r);
5091         }
5092         this.fireEvent("load", this, r, options);
5093         if(options.callback){
5094             options.callback.call(options.scope || this, r, options, true);
5095         }
5096     },
5097
5098
5099     /**
5100      * Loads data from a passed data block. A Reader which understands the format of the data
5101      * must have been configured in the constructor.
5102      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5103      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5104      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5105      */
5106     loadData : function(o, append){
5107         var r = this.reader.readRecords(o);
5108         this.loadRecords(r, {add: append}, true);
5109     },
5110
5111     /**
5112      * Gets the number of cached records.
5113      * <p>
5114      * <em>If using paging, this may not be the total size of the dataset. If the data object
5115      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5116      * the data set size</em>
5117      */
5118     getCount : function(){
5119         return this.data.length || 0;
5120     },
5121
5122     /**
5123      * Gets the total number of records in the dataset as returned by the server.
5124      * <p>
5125      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5126      * the dataset size</em>
5127      */
5128     getTotalCount : function(){
5129         return this.totalLength || 0;
5130     },
5131
5132     /**
5133      * Returns the sort state of the Store as an object with two properties:
5134      * <pre><code>
5135  field {String} The name of the field by which the Records are sorted
5136  direction {String} The sort order, "ASC" or "DESC"
5137      * </code></pre>
5138      */
5139     getSortState : function(){
5140         return this.sortInfo;
5141     },
5142
5143     // private
5144     applySort : function(){
5145         if(this.sortInfo && !this.remoteSort){
5146             var s = this.sortInfo, f = s.field;
5147             var st = this.fields.get(f).sortType;
5148             var fn = function(r1, r2){
5149                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5150                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5151             };
5152             this.data.sort(s.direction, fn);
5153             if(this.snapshot && this.snapshot != this.data){
5154                 this.snapshot.sort(s.direction, fn);
5155             }
5156         }
5157     },
5158
5159     /**
5160      * Sets the default sort column and order to be used by the next load operation.
5161      * @param {String} fieldName The name of the field to sort by.
5162      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5163      */
5164     setDefaultSort : function(field, dir){
5165         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5166     },
5167
5168     /**
5169      * Sort the Records.
5170      * If remote sorting is used, the sort is performed on the server, and the cache is
5171      * reloaded. If local sorting is used, the cache is sorted internally.
5172      * @param {String} fieldName The name of the field to sort by.
5173      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5174      */
5175     sort : function(fieldName, dir){
5176         var f = this.fields.get(fieldName);
5177         if(!dir){
5178             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
5179             
5180             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
5181                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5182             }else{
5183                 dir = f.sortDir;
5184             }
5185         }
5186         this.sortToggle[f.name] = dir;
5187         this.sortInfo = {field: f.name, direction: dir};
5188         if(!this.remoteSort){
5189             this.applySort();
5190             this.fireEvent("datachanged", this);
5191         }else{
5192             this.load(this.lastOptions);
5193         }
5194     },
5195
5196     /**
5197      * Calls the specified function for each of the Records in the cache.
5198      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5199      * Returning <em>false</em> aborts and exits the iteration.
5200      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5201      */
5202     each : function(fn, scope){
5203         this.data.each(fn, scope);
5204     },
5205
5206     /**
5207      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5208      * (e.g., during paging).
5209      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5210      */
5211     getModifiedRecords : function(){
5212         return this.modified;
5213     },
5214
5215     // private
5216     createFilterFn : function(property, value, anyMatch){
5217         if(!value.exec){ // not a regex
5218             value = String(value);
5219             if(value.length == 0){
5220                 return false;
5221             }
5222             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5223         }
5224         return function(r){
5225             return value.test(r.data[property]);
5226         };
5227     },
5228
5229     /**
5230      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5231      * @param {String} property A field on your records
5232      * @param {Number} start The record index to start at (defaults to 0)
5233      * @param {Number} end The last record index to include (defaults to length - 1)
5234      * @return {Number} The sum
5235      */
5236     sum : function(property, start, end){
5237         var rs = this.data.items, v = 0;
5238         start = start || 0;
5239         end = (end || end === 0) ? end : rs.length-1;
5240
5241         for(var i = start; i <= end; i++){
5242             v += (rs[i].data[property] || 0);
5243         }
5244         return v;
5245     },
5246
5247     /**
5248      * Filter the records by a specified property.
5249      * @param {String} field A field on your records
5250      * @param {String/RegExp} value Either a string that the field
5251      * should start with or a RegExp to test against the field
5252      * @param {Boolean} anyMatch True to match any part not just the beginning
5253      */
5254     filter : function(property, value, anyMatch){
5255         var fn = this.createFilterFn(property, value, anyMatch);
5256         return fn ? this.filterBy(fn) : this.clearFilter();
5257     },
5258
5259     /**
5260      * Filter by a function. The specified function will be called with each
5261      * record in this data source. If the function returns true the record is included,
5262      * otherwise it is filtered.
5263      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5264      * @param {Object} scope (optional) The scope of the function (defaults to this)
5265      */
5266     filterBy : function(fn, scope){
5267         this.snapshot = this.snapshot || this.data;
5268         this.data = this.queryBy(fn, scope||this);
5269         this.fireEvent("datachanged", this);
5270     },
5271
5272     /**
5273      * Query the records by a specified property.
5274      * @param {String} field A field on your records
5275      * @param {String/RegExp} value Either a string that the field
5276      * should start with or a RegExp to test against the field
5277      * @param {Boolean} anyMatch True to match any part not just the beginning
5278      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5279      */
5280     query : function(property, value, anyMatch){
5281         var fn = this.createFilterFn(property, value, anyMatch);
5282         return fn ? this.queryBy(fn) : this.data.clone();
5283     },
5284
5285     /**
5286      * Query by a function. The specified function will be called with each
5287      * record in this data source. If the function returns true the record is included
5288      * in the results.
5289      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5290      * @param {Object} scope (optional) The scope of the function (defaults to this)
5291       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5292      **/
5293     queryBy : function(fn, scope){
5294         var data = this.snapshot || this.data;
5295         return data.filterBy(fn, scope||this);
5296     },
5297
5298     /**
5299      * Collects unique values for a particular dataIndex from this store.
5300      * @param {String} dataIndex The property to collect
5301      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5302      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5303      * @return {Array} An array of the unique values
5304      **/
5305     collect : function(dataIndex, allowNull, bypassFilter){
5306         var d = (bypassFilter === true && this.snapshot) ?
5307                 this.snapshot.items : this.data.items;
5308         var v, sv, r = [], l = {};
5309         for(var i = 0, len = d.length; i < len; i++){
5310             v = d[i].data[dataIndex];
5311             sv = String(v);
5312             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5313                 l[sv] = true;
5314                 r[r.length] = v;
5315             }
5316         }
5317         return r;
5318     },
5319
5320     /**
5321      * Revert to a view of the Record cache with no filtering applied.
5322      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5323      */
5324     clearFilter : function(suppressEvent){
5325         if(this.snapshot && this.snapshot != this.data){
5326             this.data = this.snapshot;
5327             delete this.snapshot;
5328             if(suppressEvent !== true){
5329                 this.fireEvent("datachanged", this);
5330             }
5331         }
5332     },
5333
5334     // private
5335     afterEdit : function(record){
5336         if(this.modified.indexOf(record) == -1){
5337             this.modified.push(record);
5338         }
5339         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5340     },
5341     
5342     // private
5343     afterReject : function(record){
5344         this.modified.remove(record);
5345         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5346     },
5347
5348     // private
5349     afterCommit : function(record){
5350         this.modified.remove(record);
5351         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5352     },
5353
5354     /**
5355      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5356      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5357      */
5358     commitChanges : function(){
5359         var m = this.modified.slice(0);
5360         this.modified = [];
5361         for(var i = 0, len = m.length; i < len; i++){
5362             m[i].commit();
5363         }
5364     },
5365
5366     /**
5367      * Cancel outstanding changes on all changed records.
5368      */
5369     rejectChanges : function(){
5370         var m = this.modified.slice(0);
5371         this.modified = [];
5372         for(var i = 0, len = m.length; i < len; i++){
5373             m[i].reject();
5374         }
5375     },
5376
5377     onMetaChange : function(meta, rtype, o){
5378         this.recordType = rtype;
5379         this.fields = rtype.prototype.fields;
5380         delete this.snapshot;
5381         this.sortInfo = meta.sortInfo || this.sortInfo;
5382         this.modified = [];
5383         this.fireEvent('metachange', this, this.reader.meta);
5384     }
5385 });/*
5386  * Based on:
5387  * Ext JS Library 1.1.1
5388  * Copyright(c) 2006-2007, Ext JS, LLC.
5389  *
5390  * Originally Released Under LGPL - original licence link has changed is not relivant.
5391  *
5392  * Fork - LGPL
5393  * <script type="text/javascript">
5394  */
5395
5396 /**
5397  * @class Roo.data.SimpleStore
5398  * @extends Roo.data.Store
5399  * Small helper class to make creating Stores from Array data easier.
5400  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5401  * @cfg {Array} fields An array of field definition objects, or field name strings.
5402  * @cfg {Array} data The multi-dimensional array of data
5403  * @constructor
5404  * @param {Object} config
5405  */
5406 Roo.data.SimpleStore = function(config){
5407     Roo.data.SimpleStore.superclass.constructor.call(this, {
5408         isLocal : true,
5409         reader: new Roo.data.ArrayReader({
5410                 id: config.id
5411             },
5412             Roo.data.Record.create(config.fields)
5413         ),
5414         proxy : new Roo.data.MemoryProxy(config.data)
5415     });
5416     this.load();
5417 };
5418 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5419  * Based on:
5420  * Ext JS Library 1.1.1
5421  * Copyright(c) 2006-2007, Ext JS, LLC.
5422  *
5423  * Originally Released Under LGPL - original licence link has changed is not relivant.
5424  *
5425  * Fork - LGPL
5426  * <script type="text/javascript">
5427  */
5428
5429 /**
5430 /**
5431  * @extends Roo.data.Store
5432  * @class Roo.data.JsonStore
5433  * Small helper class to make creating Stores for JSON data easier. <br/>
5434 <pre><code>
5435 var store = new Roo.data.JsonStore({
5436     url: 'get-images.php',
5437     root: 'images',
5438     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5439 });
5440 </code></pre>
5441  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5442  * JsonReader and HttpProxy (unless inline data is provided).</b>
5443  * @cfg {Array} fields An array of field definition objects, or field name strings.
5444  * @constructor
5445  * @param {Object} config
5446  */
5447 Roo.data.JsonStore = function(c){
5448     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5449         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5450         reader: new Roo.data.JsonReader(c, c.fields)
5451     }));
5452 };
5453 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5454  * Based on:
5455  * Ext JS Library 1.1.1
5456  * Copyright(c) 2006-2007, Ext JS, LLC.
5457  *
5458  * Originally Released Under LGPL - original licence link has changed is not relivant.
5459  *
5460  * Fork - LGPL
5461  * <script type="text/javascript">
5462  */
5463
5464  
5465 Roo.data.Field = function(config){
5466     if(typeof config == "string"){
5467         config = {name: config};
5468     }
5469     Roo.apply(this, config);
5470     
5471     if(!this.type){
5472         this.type = "auto";
5473     }
5474     
5475     var st = Roo.data.SortTypes;
5476     // named sortTypes are supported, here we look them up
5477     if(typeof this.sortType == "string"){
5478         this.sortType = st[this.sortType];
5479     }
5480     
5481     // set default sortType for strings and dates
5482     if(!this.sortType){
5483         switch(this.type){
5484             case "string":
5485                 this.sortType = st.asUCString;
5486                 break;
5487             case "date":
5488                 this.sortType = st.asDate;
5489                 break;
5490             default:
5491                 this.sortType = st.none;
5492         }
5493     }
5494
5495     // define once
5496     var stripRe = /[\$,%]/g;
5497
5498     // prebuilt conversion function for this field, instead of
5499     // switching every time we're reading a value
5500     if(!this.convert){
5501         var cv, dateFormat = this.dateFormat;
5502         switch(this.type){
5503             case "":
5504             case "auto":
5505             case undefined:
5506                 cv = function(v){ return v; };
5507                 break;
5508             case "string":
5509                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5510                 break;
5511             case "int":
5512                 cv = function(v){
5513                     return v !== undefined && v !== null && v !== '' ?
5514                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5515                     };
5516                 break;
5517             case "float":
5518                 cv = function(v){
5519                     return v !== undefined && v !== null && v !== '' ?
5520                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5521                     };
5522                 break;
5523             case "bool":
5524             case "boolean":
5525                 cv = function(v){ return v === true || v === "true" || v == 1; };
5526                 break;
5527             case "date":
5528                 cv = function(v){
5529                     if(!v){
5530                         return '';
5531                     }
5532                     if(v instanceof Date){
5533                         return v;
5534                     }
5535                     if(dateFormat){
5536                         if(dateFormat == "timestamp"){
5537                             return new Date(v*1000);
5538                         }
5539                         return Date.parseDate(v, dateFormat);
5540                     }
5541                     var parsed = Date.parse(v);
5542                     return parsed ? new Date(parsed) : null;
5543                 };
5544              break;
5545             
5546         }
5547         this.convert = cv;
5548     }
5549 };
5550
5551 Roo.data.Field.prototype = {
5552     dateFormat: null,
5553     defaultValue: "",
5554     mapping: null,
5555     sortType : null,
5556     sortDir : "ASC"
5557 };/*
5558  * Based on:
5559  * Ext JS Library 1.1.1
5560  * Copyright(c) 2006-2007, Ext JS, LLC.
5561  *
5562  * Originally Released Under LGPL - original licence link has changed is not relivant.
5563  *
5564  * Fork - LGPL
5565  * <script type="text/javascript">
5566  */
5567  
5568 // Base class for reading structured data from a data source.  This class is intended to be
5569 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5570
5571 /**
5572  * @class Roo.data.DataReader
5573  * Base class for reading structured data from a data source.  This class is intended to be
5574  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5575  */
5576
5577 Roo.data.DataReader = function(meta, recordType){
5578     
5579     this.meta = meta;
5580     
5581     this.recordType = recordType instanceof Array ? 
5582         Roo.data.Record.create(recordType) : recordType;
5583 };
5584
5585 Roo.data.DataReader.prototype = {
5586      /**
5587      * Create an empty record
5588      * @param {Object} data (optional) - overlay some values
5589      * @return {Roo.data.Record} record created.
5590      */
5591     newRow :  function(d) {
5592         var da =  {};
5593         this.recordType.prototype.fields.each(function(c) {
5594             switch( c.type) {
5595                 case 'int' : da[c.name] = 0; break;
5596                 case 'date' : da[c.name] = new Date(); break;
5597                 case 'float' : da[c.name] = 0.0; break;
5598                 case 'boolean' : da[c.name] = false; break;
5599                 default : da[c.name] = ""; break;
5600             }
5601             
5602         });
5603         return new this.recordType(Roo.apply(da, d));
5604     }
5605     
5606 };/*
5607  * Based on:
5608  * Ext JS Library 1.1.1
5609  * Copyright(c) 2006-2007, Ext JS, LLC.
5610  *
5611  * Originally Released Under LGPL - original licence link has changed is not relivant.
5612  *
5613  * Fork - LGPL
5614  * <script type="text/javascript">
5615  */
5616
5617 /**
5618  * @class Roo.data.DataProxy
5619  * @extends Roo.data.Observable
5620  * This class is an abstract base class for implementations which provide retrieval of
5621  * unformatted data objects.<br>
5622  * <p>
5623  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5624  * (of the appropriate type which knows how to parse the data object) to provide a block of
5625  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5626  * <p>
5627  * Custom implementations must implement the load method as described in
5628  * {@link Roo.data.HttpProxy#load}.
5629  */
5630 Roo.data.DataProxy = function(){
5631     this.addEvents({
5632         /**
5633          * @event beforeload
5634          * Fires before a network request is made to retrieve a data object.
5635          * @param {Object} This DataProxy object.
5636          * @param {Object} params The params parameter to the load function.
5637          */
5638         beforeload : true,
5639         /**
5640          * @event load
5641          * Fires before the load method's callback is called.
5642          * @param {Object} This DataProxy object.
5643          * @param {Object} o The data object.
5644          * @param {Object} arg The callback argument object passed to the load function.
5645          */
5646         load : true,
5647         /**
5648          * @event loadexception
5649          * Fires if an Exception occurs during data retrieval.
5650          * @param {Object} This DataProxy object.
5651          * @param {Object} o The data object.
5652          * @param {Object} arg The callback argument object passed to the load function.
5653          * @param {Object} e The Exception.
5654          */
5655         loadexception : true
5656     });
5657     Roo.data.DataProxy.superclass.constructor.call(this);
5658 };
5659
5660 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5661
5662     /**
5663      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5664      */
5665 /*
5666  * Based on:
5667  * Ext JS Library 1.1.1
5668  * Copyright(c) 2006-2007, Ext JS, LLC.
5669  *
5670  * Originally Released Under LGPL - original licence link has changed is not relivant.
5671  *
5672  * Fork - LGPL
5673  * <script type="text/javascript">
5674  */
5675 /**
5676  * @class Roo.data.MemoryProxy
5677  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5678  * to the Reader when its load method is called.
5679  * @constructor
5680  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5681  */
5682 Roo.data.MemoryProxy = function(data){
5683     if (data.data) {
5684         data = data.data;
5685     }
5686     Roo.data.MemoryProxy.superclass.constructor.call(this);
5687     this.data = data;
5688 };
5689
5690 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5691     /**
5692      * Load data from the requested source (in this case an in-memory
5693      * data object passed to the constructor), read the data object into
5694      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5695      * process that block using the passed callback.
5696      * @param {Object} params This parameter is not used by the MemoryProxy class.
5697      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5698      * object into a block of Roo.data.Records.
5699      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5700      * The function must be passed <ul>
5701      * <li>The Record block object</li>
5702      * <li>The "arg" argument from the load function</li>
5703      * <li>A boolean success indicator</li>
5704      * </ul>
5705      * @param {Object} scope The scope in which to call the callback
5706      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5707      */
5708     load : function(params, reader, callback, scope, arg){
5709         params = params || {};
5710         var result;
5711         try {
5712             result = reader.readRecords(this.data);
5713         }catch(e){
5714             this.fireEvent("loadexception", this, arg, null, e);
5715             callback.call(scope, null, arg, false);
5716             return;
5717         }
5718         callback.call(scope, result, arg, true);
5719     },
5720     
5721     // private
5722     update : function(params, records){
5723         
5724     }
5725 });/*
5726  * Based on:
5727  * Ext JS Library 1.1.1
5728  * Copyright(c) 2006-2007, Ext JS, LLC.
5729  *
5730  * Originally Released Under LGPL - original licence link has changed is not relivant.
5731  *
5732  * Fork - LGPL
5733  * <script type="text/javascript">
5734  */
5735 /**
5736  * @class Roo.data.HttpProxy
5737  * @extends Roo.data.DataProxy
5738  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5739  * configured to reference a certain URL.<br><br>
5740  * <p>
5741  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5742  * from which the running page was served.<br><br>
5743  * <p>
5744  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5745  * <p>
5746  * Be aware that to enable the browser to parse an XML document, the server must set
5747  * the Content-Type header in the HTTP response to "text/xml".
5748  * @constructor
5749  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5750  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5751  * will be used to make the request.
5752  */
5753 Roo.data.HttpProxy = function(conn){
5754     Roo.data.HttpProxy.superclass.constructor.call(this);
5755     // is conn a conn config or a real conn?
5756     this.conn = conn;
5757     this.useAjax = !conn || !conn.events;
5758   
5759 };
5760
5761 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5762     // thse are take from connection...
5763     
5764     /**
5765      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5766      */
5767     /**
5768      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5769      * extra parameters to each request made by this object. (defaults to undefined)
5770      */
5771     /**
5772      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5773      *  to each request made by this object. (defaults to undefined)
5774      */
5775     /**
5776      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
5777      */
5778     /**
5779      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5780      */
5781      /**
5782      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5783      * @type Boolean
5784      */
5785   
5786
5787     /**
5788      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5789      * @type Boolean
5790      */
5791     /**
5792      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5793      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5794      * a finer-grained basis than the DataProxy events.
5795      */
5796     getConnection : function(){
5797         return this.useAjax ? Roo.Ajax : this.conn;
5798     },
5799
5800     /**
5801      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5802      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5803      * process that block using the passed callback.
5804      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5805      * for the request to the remote server.
5806      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5807      * object into a block of Roo.data.Records.
5808      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5809      * The function must be passed <ul>
5810      * <li>The Record block object</li>
5811      * <li>The "arg" argument from the load function</li>
5812      * <li>A boolean success indicator</li>
5813      * </ul>
5814      * @param {Object} scope The scope in which to call the callback
5815      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5816      */
5817     load : function(params, reader, callback, scope, arg){
5818         if(this.fireEvent("beforeload", this, params) !== false){
5819             var  o = {
5820                 params : params || {},
5821                 request: {
5822                     callback : callback,
5823                     scope : scope,
5824                     arg : arg
5825                 },
5826                 reader: reader,
5827                 callback : this.loadResponse,
5828                 scope: this
5829             };
5830             if(this.useAjax){
5831                 Roo.applyIf(o, this.conn);
5832                 if(this.activeRequest){
5833                     Roo.Ajax.abort(this.activeRequest);
5834                 }
5835                 this.activeRequest = Roo.Ajax.request(o);
5836             }else{
5837                 this.conn.request(o);
5838             }
5839         }else{
5840             callback.call(scope||this, null, arg, false);
5841         }
5842     },
5843
5844     // private
5845     loadResponse : function(o, success, response){
5846         delete this.activeRequest;
5847         if(!success){
5848             this.fireEvent("loadexception", this, o, response);
5849             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5850             return;
5851         }
5852         var result;
5853         try {
5854             result = o.reader.read(response);
5855         }catch(e){
5856             this.fireEvent("loadexception", this, o, response, e);
5857             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5858             return;
5859         }
5860         
5861         this.fireEvent("load", this, o, o.request.arg);
5862         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5863     },
5864
5865     // private
5866     update : function(dataSet){
5867
5868     },
5869
5870     // private
5871     updateResponse : function(dataSet){
5872
5873     }
5874 });/*
5875  * Based on:
5876  * Ext JS Library 1.1.1
5877  * Copyright(c) 2006-2007, Ext JS, LLC.
5878  *
5879  * Originally Released Under LGPL - original licence link has changed is not relivant.
5880  *
5881  * Fork - LGPL
5882  * <script type="text/javascript">
5883  */
5884
5885 /**
5886  * @class Roo.data.ScriptTagProxy
5887  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5888  * other than the originating domain of the running page.<br><br>
5889  * <p>
5890  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
5891  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5892  * <p>
5893  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5894  * source code that is used as the source inside a &lt;script> tag.<br><br>
5895  * <p>
5896  * In order for the browser to process the returned data, the server must wrap the data object
5897  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5898  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5899  * depending on whether the callback name was passed:
5900  * <p>
5901  * <pre><code>
5902 boolean scriptTag = false;
5903 String cb = request.getParameter("callback");
5904 if (cb != null) {
5905     scriptTag = true;
5906     response.setContentType("text/javascript");
5907 } else {
5908     response.setContentType("application/x-json");
5909 }
5910 Writer out = response.getWriter();
5911 if (scriptTag) {
5912     out.write(cb + "(");
5913 }
5914 out.print(dataBlock.toJsonString());
5915 if (scriptTag) {
5916     out.write(");");
5917 }
5918 </pre></code>
5919  *
5920  * @constructor
5921  * @param {Object} config A configuration object.
5922  */
5923 Roo.data.ScriptTagProxy = function(config){
5924     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5925     Roo.apply(this, config);
5926     this.head = document.getElementsByTagName("head")[0];
5927 };
5928
5929 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5930
5931 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5932     /**
5933      * @cfg {String} url The URL from which to request the data object.
5934      */
5935     /**
5936      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5937      */
5938     timeout : 30000,
5939     /**
5940      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5941      * the server the name of the callback function set up by the load call to process the returned data object.
5942      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5943      * javascript output which calls this named function passing the data object as its only parameter.
5944      */
5945     callbackParam : "callback",
5946     /**
5947      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5948      * name to the request.
5949      */
5950     nocache : true,
5951
5952     /**
5953      * Load data from the configured URL, read the data object into
5954      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5955      * process that block using the passed callback.
5956      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5957      * for the request to the remote server.
5958      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5959      * object into a block of Roo.data.Records.
5960      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5961      * The function must be passed <ul>
5962      * <li>The Record block object</li>
5963      * <li>The "arg" argument from the load function</li>
5964      * <li>A boolean success indicator</li>
5965      * </ul>
5966      * @param {Object} scope The scope in which to call the callback
5967      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5968      */
5969     load : function(params, reader, callback, scope, arg){
5970         if(this.fireEvent("beforeload", this, params) !== false){
5971
5972             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
5973
5974             var url = this.url;
5975             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
5976             if(this.nocache){
5977                 url += "&_dc=" + (new Date().getTime());
5978             }
5979             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
5980             var trans = {
5981                 id : transId,
5982                 cb : "stcCallback"+transId,
5983                 scriptId : "stcScript"+transId,
5984                 params : params,
5985                 arg : arg,
5986                 url : url,
5987                 callback : callback,
5988                 scope : scope,
5989                 reader : reader
5990             };
5991             var conn = this;
5992
5993             window[trans.cb] = function(o){
5994                 conn.handleResponse(o, trans);
5995             };
5996
5997             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
5998
5999             if(this.autoAbort !== false){
6000                 this.abort();
6001             }
6002
6003             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
6004
6005             var script = document.createElement("script");
6006             script.setAttribute("src", url);
6007             script.setAttribute("type", "text/javascript");
6008             script.setAttribute("id", trans.scriptId);
6009             this.head.appendChild(script);
6010
6011             this.trans = trans;
6012         }else{
6013             callback.call(scope||this, null, arg, false);
6014         }
6015     },
6016
6017     // private
6018     isLoading : function(){
6019         return this.trans ? true : false;
6020     },
6021
6022     /**
6023      * Abort the current server request.
6024      */
6025     abort : function(){
6026         if(this.isLoading()){
6027             this.destroyTrans(this.trans);
6028         }
6029     },
6030
6031     // private
6032     destroyTrans : function(trans, isLoaded){
6033         this.head.removeChild(document.getElementById(trans.scriptId));
6034         clearTimeout(trans.timeoutId);
6035         if(isLoaded){
6036             window[trans.cb] = undefined;
6037             try{
6038                 delete window[trans.cb];
6039             }catch(e){}
6040         }else{
6041             // if hasn't been loaded, wait for load to remove it to prevent script error
6042             window[trans.cb] = function(){
6043                 window[trans.cb] = undefined;
6044                 try{
6045                     delete window[trans.cb];
6046                 }catch(e){}
6047             };
6048         }
6049     },
6050
6051     // private
6052     handleResponse : function(o, trans){
6053         this.trans = false;
6054         this.destroyTrans(trans, true);
6055         var result;
6056         try {
6057             result = trans.reader.readRecords(o);
6058         }catch(e){
6059             this.fireEvent("loadexception", this, o, trans.arg, e);
6060             trans.callback.call(trans.scope||window, null, trans.arg, false);
6061             return;
6062         }
6063         this.fireEvent("load", this, o, trans.arg);
6064         trans.callback.call(trans.scope||window, result, trans.arg, true);
6065     },
6066
6067     // private
6068     handleFailure : function(trans){
6069         this.trans = false;
6070         this.destroyTrans(trans, false);
6071         this.fireEvent("loadexception", this, null, trans.arg);
6072         trans.callback.call(trans.scope||window, null, trans.arg, false);
6073     }
6074 });/*
6075  * Based on:
6076  * Ext JS Library 1.1.1
6077  * Copyright(c) 2006-2007, Ext JS, LLC.
6078  *
6079  * Originally Released Under LGPL - original licence link has changed is not relivant.
6080  *
6081  * Fork - LGPL
6082  * <script type="text/javascript">
6083  */
6084
6085 /**
6086  * @class Roo.data.JsonReader
6087  * @extends Roo.data.DataReader
6088  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6089  * based on mappings in a provided Roo.data.Record constructor.
6090  * 
6091  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6092  * in the reply previously. 
6093  * 
6094  * <p>
6095  * Example code:
6096  * <pre><code>
6097 var RecordDef = Roo.data.Record.create([
6098     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6099     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6100 ]);
6101 var myReader = new Roo.data.JsonReader({
6102     totalProperty: "results",    // The property which contains the total dataset size (optional)
6103     root: "rows",                // The property which contains an Array of row objects
6104     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6105 }, RecordDef);
6106 </code></pre>
6107  * <p>
6108  * This would consume a JSON file like this:
6109  * <pre><code>
6110 { 'results': 2, 'rows': [
6111     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6112     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6113 }
6114 </code></pre>
6115  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6116  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6117  * paged from the remote server.
6118  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6119  * @cfg {String} root name of the property which contains the Array of row objects.
6120  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6121  * @constructor
6122  * Create a new JsonReader
6123  * @param {Object} meta Metadata configuration options
6124  * @param {Object} recordType Either an Array of field definition objects,
6125  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6126  */
6127 Roo.data.JsonReader = function(meta, recordType){
6128     
6129     meta = meta || {};
6130     // set some defaults:
6131     Roo.applyIf(meta, {
6132         totalProperty: 'total',
6133         successProperty : 'success',
6134         root : 'data',
6135         id : 'id'
6136     });
6137     
6138     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6139 };
6140 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6141     
6142     /**
6143      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6144      * Used by Store query builder to append _requestMeta to params.
6145      * 
6146      */
6147     metaFromRemote : false,
6148     /**
6149      * This method is only used by a DataProxy which has retrieved data from a remote server.
6150      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6151      * @return {Object} data A data block which is used by an Roo.data.Store object as
6152      * a cache of Roo.data.Records.
6153      */
6154     read : function(response){
6155         var json = response.responseText;
6156        
6157         var o = /* eval:var:o */ eval("("+json+")");
6158         if(!o) {
6159             throw {message: "JsonReader.read: Json object not found"};
6160         }
6161         
6162         if(o.metaData){
6163             
6164             delete this.ef;
6165             this.metaFromRemote = true;
6166             this.meta = o.metaData;
6167             this.recordType = Roo.data.Record.create(o.metaData.fields);
6168             this.onMetaChange(this.meta, this.recordType, o);
6169         }
6170         return this.readRecords(o);
6171     },
6172
6173     // private function a store will implement
6174     onMetaChange : function(meta, recordType, o){
6175
6176     },
6177
6178     /**
6179          * @ignore
6180          */
6181     simpleAccess: function(obj, subsc) {
6182         return obj[subsc];
6183     },
6184
6185         /**
6186          * @ignore
6187          */
6188     getJsonAccessor: function(){
6189         var re = /[\[\.]/;
6190         return function(expr) {
6191             try {
6192                 return(re.test(expr))
6193                     ? new Function("obj", "return obj." + expr)
6194                     : function(obj){
6195                         return obj[expr];
6196                     };
6197             } catch(e){}
6198             return Roo.emptyFn;
6199         };
6200     }(),
6201
6202     /**
6203      * Create a data block containing Roo.data.Records from an XML document.
6204      * @param {Object} o An object which contains an Array of row objects in the property specified
6205      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6206      * which contains the total size of the dataset.
6207      * @return {Object} data A data block which is used by an Roo.data.Store object as
6208      * a cache of Roo.data.Records.
6209      */
6210     readRecords : function(o){
6211         /**
6212          * After any data loads, the raw JSON data is available for further custom processing.
6213          * @type Object
6214          */
6215         this.jsonData = o;
6216         var s = this.meta, Record = this.recordType,
6217             f = Record.prototype.fields, fi = f.items, fl = f.length;
6218
6219 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6220         if (!this.ef) {
6221             if(s.totalProperty) {
6222                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6223                 }
6224                 if(s.successProperty) {
6225                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6226                 }
6227                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6228                 if (s.id) {
6229                         var g = this.getJsonAccessor(s.id);
6230                         this.getId = function(rec) {
6231                                 var r = g(rec);
6232                                 return (r === undefined || r === "") ? null : r;
6233                         };
6234                 } else {
6235                         this.getId = function(){return null;};
6236                 }
6237             this.ef = [];
6238             for(var jj = 0; jj < fl; jj++){
6239                 f = fi[jj];
6240                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6241                 this.ef[jj] = this.getJsonAccessor(map);
6242             }
6243         }
6244
6245         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6246         if(s.totalProperty){
6247             var vt = parseInt(this.getTotal(o), 10);
6248             if(!isNaN(vt)){
6249                 totalRecords = vt;
6250             }
6251         }
6252         if(s.successProperty){
6253             var vs = this.getSuccess(o);
6254             if(vs === false || vs === 'false'){
6255                 success = false;
6256             }
6257         }
6258         var records = [];
6259             for(var i = 0; i < c; i++){
6260                     var n = root[i];
6261                 var values = {};
6262                 var id = this.getId(n);
6263                 for(var j = 0; j < fl; j++){
6264                     f = fi[j];
6265                 var v = this.ef[j](n);
6266                 if (!f.convert) {
6267                     Roo.log('missing convert for ' + f.name);
6268                     Roo.log(f);
6269                     continue;
6270                 }
6271                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6272                 }
6273                 var record = new Record(values, id);
6274                 record.json = n;
6275                 records[i] = record;
6276             }
6277             return {
6278                 success : success,
6279                 records : records,
6280                 totalRecords : totalRecords
6281             };
6282     }
6283 });/*
6284  * Based on:
6285  * Ext JS Library 1.1.1
6286  * Copyright(c) 2006-2007, Ext JS, LLC.
6287  *
6288  * Originally Released Under LGPL - original licence link has changed is not relivant.
6289  *
6290  * Fork - LGPL
6291  * <script type="text/javascript">
6292  */
6293
6294 /**
6295  * @class Roo.data.XmlReader
6296  * @extends Roo.data.DataReader
6297  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6298  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6299  * <p>
6300  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6301  * header in the HTTP response must be set to "text/xml".</em>
6302  * <p>
6303  * Example code:
6304  * <pre><code>
6305 var RecordDef = Roo.data.Record.create([
6306    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6307    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6308 ]);
6309 var myReader = new Roo.data.XmlReader({
6310    totalRecords: "results", // The element which contains the total dataset size (optional)
6311    record: "row",           // The repeated element which contains row information
6312    id: "id"                 // The element within the row that provides an ID for the record (optional)
6313 }, RecordDef);
6314 </code></pre>
6315  * <p>
6316  * This would consume an XML file like this:
6317  * <pre><code>
6318 &lt;?xml?>
6319 &lt;dataset>
6320  &lt;results>2&lt;/results>
6321  &lt;row>
6322    &lt;id>1&lt;/id>
6323    &lt;name>Bill&lt;/name>
6324    &lt;occupation>Gardener&lt;/occupation>
6325  &lt;/row>
6326  &lt;row>
6327    &lt;id>2&lt;/id>
6328    &lt;name>Ben&lt;/name>
6329    &lt;occupation>Horticulturalist&lt;/occupation>
6330  &lt;/row>
6331 &lt;/dataset>
6332 </code></pre>
6333  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6334  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6335  * paged from the remote server.
6336  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6337  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6338  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6339  * a record identifier value.
6340  * @constructor
6341  * Create a new XmlReader
6342  * @param {Object} meta Metadata configuration options
6343  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6344  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6345  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6346  */
6347 Roo.data.XmlReader = function(meta, recordType){
6348     meta = meta || {};
6349     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6350 };
6351 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6352     /**
6353      * This method is only used by a DataProxy which has retrieved data from a remote server.
6354          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6355          * to contain a method called 'responseXML' that returns an XML document object.
6356      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6357      * a cache of Roo.data.Records.
6358      */
6359     read : function(response){
6360         var doc = response.responseXML;
6361         if(!doc) {
6362             throw {message: "XmlReader.read: XML Document not available"};
6363         }
6364         return this.readRecords(doc);
6365     },
6366
6367     /**
6368      * Create a data block containing Roo.data.Records from an XML document.
6369          * @param {Object} doc A parsed XML document.
6370      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6371      * a cache of Roo.data.Records.
6372      */
6373     readRecords : function(doc){
6374         /**
6375          * After any data loads/reads, the raw XML Document is available for further custom processing.
6376          * @type XMLDocument
6377          */
6378         this.xmlData = doc;
6379         var root = doc.documentElement || doc;
6380         var q = Roo.DomQuery;
6381         var recordType = this.recordType, fields = recordType.prototype.fields;
6382         var sid = this.meta.id;
6383         var totalRecords = 0, success = true;
6384         if(this.meta.totalRecords){
6385             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6386         }
6387         
6388         if(this.meta.success){
6389             var sv = q.selectValue(this.meta.success, root, true);
6390             success = sv !== false && sv !== 'false';
6391         }
6392         var records = [];
6393         var ns = q.select(this.meta.record, root);
6394         for(var i = 0, len = ns.length; i < len; i++) {
6395                 var n = ns[i];
6396                 var values = {};
6397                 var id = sid ? q.selectValue(sid, n) : undefined;
6398                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6399                     var f = fields.items[j];
6400                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6401                     v = f.convert(v);
6402                     values[f.name] = v;
6403                 }
6404                 var record = new recordType(values, id);
6405                 record.node = n;
6406                 records[records.length] = record;
6407             }
6408
6409             return {
6410                 success : success,
6411                 records : records,
6412                 totalRecords : totalRecords || records.length
6413             };
6414     }
6415 });/*
6416  * Based on:
6417  * Ext JS Library 1.1.1
6418  * Copyright(c) 2006-2007, Ext JS, LLC.
6419  *
6420  * Originally Released Under LGPL - original licence link has changed is not relivant.
6421  *
6422  * Fork - LGPL
6423  * <script type="text/javascript">
6424  */
6425
6426 /**
6427  * @class Roo.data.ArrayReader
6428  * @extends Roo.data.DataReader
6429  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6430  * Each element of that Array represents a row of data fields. The
6431  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6432  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6433  * <p>
6434  * Example code:.
6435  * <pre><code>
6436 var RecordDef = Roo.data.Record.create([
6437     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6438     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6439 ]);
6440 var myReader = new Roo.data.ArrayReader({
6441     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6442 }, RecordDef);
6443 </code></pre>
6444  * <p>
6445  * This would consume an Array like this:
6446  * <pre><code>
6447 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6448   </code></pre>
6449  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6450  * @constructor
6451  * Create a new JsonReader
6452  * @param {Object} meta Metadata configuration options.
6453  * @param {Object} recordType Either an Array of field definition objects
6454  * as specified to {@link Roo.data.Record#create},
6455  * or an {@link Roo.data.Record} object
6456  * created using {@link Roo.data.Record#create}.
6457  */
6458 Roo.data.ArrayReader = function(meta, recordType){
6459     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6460 };
6461
6462 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6463     /**
6464      * Create a data block containing Roo.data.Records from an XML document.
6465      * @param {Object} o An Array of row objects which represents the dataset.
6466      * @return {Object} data A data block which is used by an Roo.data.Store object as
6467      * a cache of Roo.data.Records.
6468      */
6469     readRecords : function(o){
6470         var sid = this.meta ? this.meta.id : null;
6471         var recordType = this.recordType, fields = recordType.prototype.fields;
6472         var records = [];
6473         var root = o;
6474             for(var i = 0; i < root.length; i++){
6475                     var n = root[i];
6476                 var values = {};
6477                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6478                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6479                 var f = fields.items[j];
6480                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6481                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6482                 v = f.convert(v);
6483                 values[f.name] = v;
6484             }
6485                 var record = new recordType(values, id);
6486                 record.json = n;
6487                 records[records.length] = record;
6488             }
6489             return {
6490                 records : records,
6491                 totalRecords : records.length
6492             };
6493     }
6494 });/*
6495  * Based on:
6496  * Ext JS Library 1.1.1
6497  * Copyright(c) 2006-2007, Ext JS, LLC.
6498  *
6499  * Originally Released Under LGPL - original licence link has changed is not relivant.
6500  *
6501  * Fork - LGPL
6502  * <script type="text/javascript">
6503  */
6504
6505
6506 /**
6507  * @class Roo.data.Tree
6508  * @extends Roo.util.Observable
6509  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6510  * in the tree have most standard DOM functionality.
6511  * @constructor
6512  * @param {Node} root (optional) The root node
6513  */
6514 Roo.data.Tree = function(root){
6515    this.nodeHash = {};
6516    /**
6517     * The root node for this tree
6518     * @type Node
6519     */
6520    this.root = null;
6521    if(root){
6522        this.setRootNode(root);
6523    }
6524    this.addEvents({
6525        /**
6526         * @event append
6527         * Fires when a new child node is appended to a node in this tree.
6528         * @param {Tree} tree The owner tree
6529         * @param {Node} parent The parent node
6530         * @param {Node} node The newly appended node
6531         * @param {Number} index The index of the newly appended node
6532         */
6533        "append" : true,
6534        /**
6535         * @event remove
6536         * Fires when a child node is removed from a node in this tree.
6537         * @param {Tree} tree The owner tree
6538         * @param {Node} parent The parent node
6539         * @param {Node} node The child node removed
6540         */
6541        "remove" : true,
6542        /**
6543         * @event move
6544         * Fires when a node is moved to a new location in the tree
6545         * @param {Tree} tree The owner tree
6546         * @param {Node} node The node moved
6547         * @param {Node} oldParent The old parent of this node
6548         * @param {Node} newParent The new parent of this node
6549         * @param {Number} index The index it was moved to
6550         */
6551        "move" : true,
6552        /**
6553         * @event insert
6554         * Fires when a new child node is inserted in a node in this tree.
6555         * @param {Tree} tree The owner tree
6556         * @param {Node} parent The parent node
6557         * @param {Node} node The child node inserted
6558         * @param {Node} refNode The child node the node was inserted before
6559         */
6560        "insert" : true,
6561        /**
6562         * @event beforeappend
6563         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6564         * @param {Tree} tree The owner tree
6565         * @param {Node} parent The parent node
6566         * @param {Node} node The child node to be appended
6567         */
6568        "beforeappend" : true,
6569        /**
6570         * @event beforeremove
6571         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6572         * @param {Tree} tree The owner tree
6573         * @param {Node} parent The parent node
6574         * @param {Node} node The child node to be removed
6575         */
6576        "beforeremove" : true,
6577        /**
6578         * @event beforemove
6579         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6580         * @param {Tree} tree The owner tree
6581         * @param {Node} node The node being moved
6582         * @param {Node} oldParent The parent of the node
6583         * @param {Node} newParent The new parent the node is moving to
6584         * @param {Number} index The index it is being moved to
6585         */
6586        "beforemove" : true,
6587        /**
6588         * @event beforeinsert
6589         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6590         * @param {Tree} tree The owner tree
6591         * @param {Node} parent The parent node
6592         * @param {Node} node The child node to be inserted
6593         * @param {Node} refNode The child node the node is being inserted before
6594         */
6595        "beforeinsert" : true
6596    });
6597
6598     Roo.data.Tree.superclass.constructor.call(this);
6599 };
6600
6601 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6602     pathSeparator: "/",
6603
6604     proxyNodeEvent : function(){
6605         return this.fireEvent.apply(this, arguments);
6606     },
6607
6608     /**
6609      * Returns the root node for this tree.
6610      * @return {Node}
6611      */
6612     getRootNode : function(){
6613         return this.root;
6614     },
6615
6616     /**
6617      * Sets the root node for this tree.
6618      * @param {Node} node
6619      * @return {Node}
6620      */
6621     setRootNode : function(node){
6622         this.root = node;
6623         node.ownerTree = this;
6624         node.isRoot = true;
6625         this.registerNode(node);
6626         return node;
6627     },
6628
6629     /**
6630      * Gets a node in this tree by its id.
6631      * @param {String} id
6632      * @return {Node}
6633      */
6634     getNodeById : function(id){
6635         return this.nodeHash[id];
6636     },
6637
6638     registerNode : function(node){
6639         this.nodeHash[node.id] = node;
6640     },
6641
6642     unregisterNode : function(node){
6643         delete this.nodeHash[node.id];
6644     },
6645
6646     toString : function(){
6647         return "[Tree"+(this.id?" "+this.id:"")+"]";
6648     }
6649 });
6650
6651 /**
6652  * @class Roo.data.Node
6653  * @extends Roo.util.Observable
6654  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6655  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6656  * @constructor
6657  * @param {Object} attributes The attributes/config for the node
6658  */
6659 Roo.data.Node = function(attributes){
6660     /**
6661      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6662      * @type {Object}
6663      */
6664     this.attributes = attributes || {};
6665     this.leaf = this.attributes.leaf;
6666     /**
6667      * The node id. @type String
6668      */
6669     this.id = this.attributes.id;
6670     if(!this.id){
6671         this.id = Roo.id(null, "ynode-");
6672         this.attributes.id = this.id;
6673     }
6674     /**
6675      * All child nodes of this node. @type Array
6676      */
6677     this.childNodes = [];
6678     if(!this.childNodes.indexOf){ // indexOf is a must
6679         this.childNodes.indexOf = function(o){
6680             for(var i = 0, len = this.length; i < len; i++){
6681                 if(this[i] == o) {
6682                     return i;
6683                 }
6684             }
6685             return -1;
6686         };
6687     }
6688     /**
6689      * The parent node for this node. @type Node
6690      */
6691     this.parentNode = null;
6692     /**
6693      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6694      */
6695     this.firstChild = null;
6696     /**
6697      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6698      */
6699     this.lastChild = null;
6700     /**
6701      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6702      */
6703     this.previousSibling = null;
6704     /**
6705      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6706      */
6707     this.nextSibling = null;
6708
6709     this.addEvents({
6710        /**
6711         * @event append
6712         * Fires when a new child node is appended
6713         * @param {Tree} tree The owner tree
6714         * @param {Node} this This node
6715         * @param {Node} node The newly appended node
6716         * @param {Number} index The index of the newly appended node
6717         */
6718        "append" : true,
6719        /**
6720         * @event remove
6721         * Fires when a child node is removed
6722         * @param {Tree} tree The owner tree
6723         * @param {Node} this This node
6724         * @param {Node} node The removed node
6725         */
6726        "remove" : true,
6727        /**
6728         * @event move
6729         * Fires when this node is moved to a new location in the tree
6730         * @param {Tree} tree The owner tree
6731         * @param {Node} this This node
6732         * @param {Node} oldParent The old parent of this node
6733         * @param {Node} newParent The new parent of this node
6734         * @param {Number} index The index it was moved to
6735         */
6736        "move" : true,
6737        /**
6738         * @event insert
6739         * Fires when a new child node is inserted.
6740         * @param {Tree} tree The owner tree
6741         * @param {Node} this This node
6742         * @param {Node} node The child node inserted
6743         * @param {Node} refNode The child node the node was inserted before
6744         */
6745        "insert" : true,
6746        /**
6747         * @event beforeappend
6748         * Fires before a new child is appended, return false to cancel the append.
6749         * @param {Tree} tree The owner tree
6750         * @param {Node} this This node
6751         * @param {Node} node The child node to be appended
6752         */
6753        "beforeappend" : true,
6754        /**
6755         * @event beforeremove
6756         * Fires before a child is removed, return false to cancel the remove.
6757         * @param {Tree} tree The owner tree
6758         * @param {Node} this This node
6759         * @param {Node} node The child node to be removed
6760         */
6761        "beforeremove" : true,
6762        /**
6763         * @event beforemove
6764         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6765         * @param {Tree} tree The owner tree
6766         * @param {Node} this This node
6767         * @param {Node} oldParent The parent of this node
6768         * @param {Node} newParent The new parent this node is moving to
6769         * @param {Number} index The index it is being moved to
6770         */
6771        "beforemove" : true,
6772        /**
6773         * @event beforeinsert
6774         * Fires before a new child is inserted, return false to cancel the insert.
6775         * @param {Tree} tree The owner tree
6776         * @param {Node} this This node
6777         * @param {Node} node The child node to be inserted
6778         * @param {Node} refNode The child node the node is being inserted before
6779         */
6780        "beforeinsert" : true
6781    });
6782     this.listeners = this.attributes.listeners;
6783     Roo.data.Node.superclass.constructor.call(this);
6784 };
6785
6786 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6787     fireEvent : function(evtName){
6788         // first do standard event for this node
6789         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6790             return false;
6791         }
6792         // then bubble it up to the tree if the event wasn't cancelled
6793         var ot = this.getOwnerTree();
6794         if(ot){
6795             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6796                 return false;
6797             }
6798         }
6799         return true;
6800     },
6801
6802     /**
6803      * Returns true if this node is a leaf
6804      * @return {Boolean}
6805      */
6806     isLeaf : function(){
6807         return this.leaf === true;
6808     },
6809
6810     // private
6811     setFirstChild : function(node){
6812         this.firstChild = node;
6813     },
6814
6815     //private
6816     setLastChild : function(node){
6817         this.lastChild = node;
6818     },
6819
6820
6821     /**
6822      * Returns true if this node is the last child of its parent
6823      * @return {Boolean}
6824      */
6825     isLast : function(){
6826        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6827     },
6828
6829     /**
6830      * Returns true if this node is the first child of its parent
6831      * @return {Boolean}
6832      */
6833     isFirst : function(){
6834        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6835     },
6836
6837     hasChildNodes : function(){
6838         return !this.isLeaf() && this.childNodes.length > 0;
6839     },
6840
6841     /**
6842      * Insert node(s) as the last child node of this node.
6843      * @param {Node/Array} node The node or Array of nodes to append
6844      * @return {Node} The appended node if single append, or null if an array was passed
6845      */
6846     appendChild : function(node){
6847         var multi = false;
6848         if(node instanceof Array){
6849             multi = node;
6850         }else if(arguments.length > 1){
6851             multi = arguments;
6852         }
6853         // if passed an array or multiple args do them one by one
6854         if(multi){
6855             for(var i = 0, len = multi.length; i < len; i++) {
6856                 this.appendChild(multi[i]);
6857             }
6858         }else{
6859             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6860                 return false;
6861             }
6862             var index = this.childNodes.length;
6863             var oldParent = node.parentNode;
6864             // it's a move, make sure we move it cleanly
6865             if(oldParent){
6866                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6867                     return false;
6868                 }
6869                 oldParent.removeChild(node);
6870             }
6871             index = this.childNodes.length;
6872             if(index == 0){
6873                 this.setFirstChild(node);
6874             }
6875             this.childNodes.push(node);
6876             node.parentNode = this;
6877             var ps = this.childNodes[index-1];
6878             if(ps){
6879                 node.previousSibling = ps;
6880                 ps.nextSibling = node;
6881             }else{
6882                 node.previousSibling = null;
6883             }
6884             node.nextSibling = null;
6885             this.setLastChild(node);
6886             node.setOwnerTree(this.getOwnerTree());
6887             this.fireEvent("append", this.ownerTree, this, node, index);
6888             if(oldParent){
6889                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6890             }
6891             return node;
6892         }
6893     },
6894
6895     /**
6896      * Removes a child node from this node.
6897      * @param {Node} node The node to remove
6898      * @return {Node} The removed node
6899      */
6900     removeChild : function(node){
6901         var index = this.childNodes.indexOf(node);
6902         if(index == -1){
6903             return false;
6904         }
6905         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6906             return false;
6907         }
6908
6909         // remove it from childNodes collection
6910         this.childNodes.splice(index, 1);
6911
6912         // update siblings
6913         if(node.previousSibling){
6914             node.previousSibling.nextSibling = node.nextSibling;
6915         }
6916         if(node.nextSibling){
6917             node.nextSibling.previousSibling = node.previousSibling;
6918         }
6919
6920         // update child refs
6921         if(this.firstChild == node){
6922             this.setFirstChild(node.nextSibling);
6923         }
6924         if(this.lastChild == node){
6925             this.setLastChild(node.previousSibling);
6926         }
6927
6928         node.setOwnerTree(null);
6929         // clear any references from the node
6930         node.parentNode = null;
6931         node.previousSibling = null;
6932         node.nextSibling = null;
6933         this.fireEvent("remove", this.ownerTree, this, node);
6934         return node;
6935     },
6936
6937     /**
6938      * Inserts the first node before the second node in this nodes childNodes collection.
6939      * @param {Node} node The node to insert
6940      * @param {Node} refNode The node to insert before (if null the node is appended)
6941      * @return {Node} The inserted node
6942      */
6943     insertBefore : function(node, refNode){
6944         if(!refNode){ // like standard Dom, refNode can be null for append
6945             return this.appendChild(node);
6946         }
6947         // nothing to do
6948         if(node == refNode){
6949             return false;
6950         }
6951
6952         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6953             return false;
6954         }
6955         var index = this.childNodes.indexOf(refNode);
6956         var oldParent = node.parentNode;
6957         var refIndex = index;
6958
6959         // when moving internally, indexes will change after remove
6960         if(oldParent == this && this.childNodes.indexOf(node) < index){
6961             refIndex--;
6962         }
6963
6964         // it's a move, make sure we move it cleanly
6965         if(oldParent){
6966             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
6967                 return false;
6968             }
6969             oldParent.removeChild(node);
6970         }
6971         if(refIndex == 0){
6972             this.setFirstChild(node);
6973         }
6974         this.childNodes.splice(refIndex, 0, node);
6975         node.parentNode = this;
6976         var ps = this.childNodes[refIndex-1];
6977         if(ps){
6978             node.previousSibling = ps;
6979             ps.nextSibling = node;
6980         }else{
6981             node.previousSibling = null;
6982         }
6983         node.nextSibling = refNode;
6984         refNode.previousSibling = node;
6985         node.setOwnerTree(this.getOwnerTree());
6986         this.fireEvent("insert", this.ownerTree, this, node, refNode);
6987         if(oldParent){
6988             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
6989         }
6990         return node;
6991     },
6992
6993     /**
6994      * Returns the child node at the specified index.
6995      * @param {Number} index
6996      * @return {Node}
6997      */
6998     item : function(index){
6999         return this.childNodes[index];
7000     },
7001
7002     /**
7003      * Replaces one child node in this node with another.
7004      * @param {Node} newChild The replacement node
7005      * @param {Node} oldChild The node to replace
7006      * @return {Node} The replaced node
7007      */
7008     replaceChild : function(newChild, oldChild){
7009         this.insertBefore(newChild, oldChild);
7010         this.removeChild(oldChild);
7011         return oldChild;
7012     },
7013
7014     /**
7015      * Returns the index of a child node
7016      * @param {Node} node
7017      * @return {Number} The index of the node or -1 if it was not found
7018      */
7019     indexOf : function(child){
7020         return this.childNodes.indexOf(child);
7021     },
7022
7023     /**
7024      * Returns the tree this node is in.
7025      * @return {Tree}
7026      */
7027     getOwnerTree : function(){
7028         // if it doesn't have one, look for one
7029         if(!this.ownerTree){
7030             var p = this;
7031             while(p){
7032                 if(p.ownerTree){
7033                     this.ownerTree = p.ownerTree;
7034                     break;
7035                 }
7036                 p = p.parentNode;
7037             }
7038         }
7039         return this.ownerTree;
7040     },
7041
7042     /**
7043      * Returns depth of this node (the root node has a depth of 0)
7044      * @return {Number}
7045      */
7046     getDepth : function(){
7047         var depth = 0;
7048         var p = this;
7049         while(p.parentNode){
7050             ++depth;
7051             p = p.parentNode;
7052         }
7053         return depth;
7054     },
7055
7056     // private
7057     setOwnerTree : function(tree){
7058         // if it's move, we need to update everyone
7059         if(tree != this.ownerTree){
7060             if(this.ownerTree){
7061                 this.ownerTree.unregisterNode(this);
7062             }
7063             this.ownerTree = tree;
7064             var cs = this.childNodes;
7065             for(var i = 0, len = cs.length; i < len; i++) {
7066                 cs[i].setOwnerTree(tree);
7067             }
7068             if(tree){
7069                 tree.registerNode(this);
7070             }
7071         }
7072     },
7073
7074     /**
7075      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7076      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7077      * @return {String} The path
7078      */
7079     getPath : function(attr){
7080         attr = attr || "id";
7081         var p = this.parentNode;
7082         var b = [this.attributes[attr]];
7083         while(p){
7084             b.unshift(p.attributes[attr]);
7085             p = p.parentNode;
7086         }
7087         var sep = this.getOwnerTree().pathSeparator;
7088         return sep + b.join(sep);
7089     },
7090
7091     /**
7092      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7093      * function call will be the scope provided or the current node. The arguments to the function
7094      * will be the args provided or the current node. If the function returns false at any point,
7095      * the bubble is stopped.
7096      * @param {Function} fn The function to call
7097      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7098      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7099      */
7100     bubble : function(fn, scope, args){
7101         var p = this;
7102         while(p){
7103             if(fn.call(scope || p, args || p) === false){
7104                 break;
7105             }
7106             p = p.parentNode;
7107         }
7108     },
7109
7110     /**
7111      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7112      * function call will be the scope provided or the current node. The arguments to the function
7113      * will be the args provided or the current node. If the function returns false at any point,
7114      * the cascade is stopped on that branch.
7115      * @param {Function} fn The function to call
7116      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7117      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7118      */
7119     cascade : function(fn, scope, args){
7120         if(fn.call(scope || this, args || this) !== false){
7121             var cs = this.childNodes;
7122             for(var i = 0, len = cs.length; i < len; i++) {
7123                 cs[i].cascade(fn, scope, args);
7124             }
7125         }
7126     },
7127
7128     /**
7129      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7130      * function call will be the scope provided or the current node. The arguments to the function
7131      * will be the args provided or the current node. If the function returns false at any point,
7132      * the iteration stops.
7133      * @param {Function} fn The function to call
7134      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7135      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7136      */
7137     eachChild : function(fn, scope, args){
7138         var cs = this.childNodes;
7139         for(var i = 0, len = cs.length; i < len; i++) {
7140                 if(fn.call(scope || this, args || cs[i]) === false){
7141                     break;
7142                 }
7143         }
7144     },
7145
7146     /**
7147      * Finds the first child that has the attribute with the specified value.
7148      * @param {String} attribute The attribute name
7149      * @param {Mixed} value The value to search for
7150      * @return {Node} The found child or null if none was found
7151      */
7152     findChild : function(attribute, value){
7153         var cs = this.childNodes;
7154         for(var i = 0, len = cs.length; i < len; i++) {
7155                 if(cs[i].attributes[attribute] == value){
7156                     return cs[i];
7157                 }
7158         }
7159         return null;
7160     },
7161
7162     /**
7163      * Finds the first child by a custom function. The child matches if the function passed
7164      * returns true.
7165      * @param {Function} fn
7166      * @param {Object} scope (optional)
7167      * @return {Node} The found child or null if none was found
7168      */
7169     findChildBy : function(fn, scope){
7170         var cs = this.childNodes;
7171         for(var i = 0, len = cs.length; i < len; i++) {
7172                 if(fn.call(scope||cs[i], cs[i]) === true){
7173                     return cs[i];
7174                 }
7175         }
7176         return null;
7177     },
7178
7179     /**
7180      * Sorts this nodes children using the supplied sort function
7181      * @param {Function} fn
7182      * @param {Object} scope (optional)
7183      */
7184     sort : function(fn, scope){
7185         var cs = this.childNodes;
7186         var len = cs.length;
7187         if(len > 0){
7188             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7189             cs.sort(sortFn);
7190             for(var i = 0; i < len; i++){
7191                 var n = cs[i];
7192                 n.previousSibling = cs[i-1];
7193                 n.nextSibling = cs[i+1];
7194                 if(i == 0){
7195                     this.setFirstChild(n);
7196                 }
7197                 if(i == len-1){
7198                     this.setLastChild(n);
7199                 }
7200             }
7201         }
7202     },
7203
7204     /**
7205      * Returns true if this node is an ancestor (at any point) of the passed node.
7206      * @param {Node} node
7207      * @return {Boolean}
7208      */
7209     contains : function(node){
7210         return node.isAncestor(this);
7211     },
7212
7213     /**
7214      * Returns true if the passed node is an ancestor (at any point) of this node.
7215      * @param {Node} node
7216      * @return {Boolean}
7217      */
7218     isAncestor : function(node){
7219         var p = this.parentNode;
7220         while(p){
7221             if(p == node){
7222                 return true;
7223             }
7224             p = p.parentNode;
7225         }
7226         return false;
7227     },
7228
7229     toString : function(){
7230         return "[Node"+(this.id?" "+this.id:"")+"]";
7231     }
7232 });/*
7233  * Based on:
7234  * Ext JS Library 1.1.1
7235  * Copyright(c) 2006-2007, Ext JS, LLC.
7236  *
7237  * Originally Released Under LGPL - original licence link has changed is not relivant.
7238  *
7239  * Fork - LGPL
7240  * <script type="text/javascript">
7241  */
7242  
7243
7244 /**
7245  * @class Roo.ComponentMgr
7246  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7247  * @singleton
7248  */
7249 Roo.ComponentMgr = function(){
7250     var all = new Roo.util.MixedCollection();
7251
7252     return {
7253         /**
7254          * Registers a component.
7255          * @param {Roo.Component} c The component
7256          */
7257         register : function(c){
7258             all.add(c);
7259         },
7260
7261         /**
7262          * Unregisters a component.
7263          * @param {Roo.Component} c The component
7264          */
7265         unregister : function(c){
7266             all.remove(c);
7267         },
7268
7269         /**
7270          * Returns a component by id
7271          * @param {String} id The component id
7272          */
7273         get : function(id){
7274             return all.get(id);
7275         },
7276
7277         /**
7278          * Registers a function that will be called when a specified component is added to ComponentMgr
7279          * @param {String} id The component id
7280          * @param {Funtction} fn The callback function
7281          * @param {Object} scope The scope of the callback
7282          */
7283         onAvailable : function(id, fn, scope){
7284             all.on("add", function(index, o){
7285                 if(o.id == id){
7286                     fn.call(scope || o, o);
7287                     all.un("add", fn, scope);
7288                 }
7289             });
7290         }
7291     };
7292 }();/*
7293  * Based on:
7294  * Ext JS Library 1.1.1
7295  * Copyright(c) 2006-2007, Ext JS, LLC.
7296  *
7297  * Originally Released Under LGPL - original licence link has changed is not relivant.
7298  *
7299  * Fork - LGPL
7300  * <script type="text/javascript">
7301  */
7302  
7303 /**
7304  * @class Roo.Component
7305  * @extends Roo.util.Observable
7306  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7307  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7308  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7309  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7310  * All visual components (widgets) that require rendering into a layout should subclass Component.
7311  * @constructor
7312  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7313  * 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
7314  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7315  */
7316 Roo.Component = function(config){
7317     config = config || {};
7318     if(config.tagName || config.dom || typeof config == "string"){ // element object
7319         config = {el: config, id: config.id || config};
7320     }
7321     this.initialConfig = config;
7322
7323     Roo.apply(this, config);
7324     this.addEvents({
7325         /**
7326          * @event disable
7327          * Fires after the component is disabled.
7328              * @param {Roo.Component} this
7329              */
7330         disable : true,
7331         /**
7332          * @event enable
7333          * Fires after the component is enabled.
7334              * @param {Roo.Component} this
7335              */
7336         enable : true,
7337         /**
7338          * @event beforeshow
7339          * Fires before the component is shown.  Return false to stop the show.
7340              * @param {Roo.Component} this
7341              */
7342         beforeshow : true,
7343         /**
7344          * @event show
7345          * Fires after the component is shown.
7346              * @param {Roo.Component} this
7347              */
7348         show : true,
7349         /**
7350          * @event beforehide
7351          * Fires before the component is hidden. Return false to stop the hide.
7352              * @param {Roo.Component} this
7353              */
7354         beforehide : true,
7355         /**
7356          * @event hide
7357          * Fires after the component is hidden.
7358              * @param {Roo.Component} this
7359              */
7360         hide : true,
7361         /**
7362          * @event beforerender
7363          * Fires before the component is rendered. Return false to stop the render.
7364              * @param {Roo.Component} this
7365              */
7366         beforerender : true,
7367         /**
7368          * @event render
7369          * Fires after the component is rendered.
7370              * @param {Roo.Component} this
7371              */
7372         render : true,
7373         /**
7374          * @event beforedestroy
7375          * Fires before the component is destroyed. Return false to stop the destroy.
7376              * @param {Roo.Component} this
7377              */
7378         beforedestroy : true,
7379         /**
7380          * @event destroy
7381          * Fires after the component is destroyed.
7382              * @param {Roo.Component} this
7383              */
7384         destroy : true
7385     });
7386     if(!this.id){
7387         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7388     }
7389     Roo.ComponentMgr.register(this);
7390     Roo.Component.superclass.constructor.call(this);
7391     this.initComponent();
7392     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7393         this.render(this.renderTo);
7394         delete this.renderTo;
7395     }
7396 };
7397
7398 /** @private */
7399 Roo.Component.AUTO_ID = 1000;
7400
7401 Roo.extend(Roo.Component, Roo.util.Observable, {
7402     /**
7403      * @scope Roo.Component.prototype
7404      * @type {Boolean}
7405      * true if this component is hidden. Read-only.
7406      */
7407     hidden : false,
7408     /**
7409      * @type {Boolean}
7410      * true if this component is disabled. Read-only.
7411      */
7412     disabled : false,
7413     /**
7414      * @type {Boolean}
7415      * true if this component has been rendered. Read-only.
7416      */
7417     rendered : false,
7418     
7419     /** @cfg {String} disableClass
7420      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7421      */
7422     disabledClass : "x-item-disabled",
7423         /** @cfg {Boolean} allowDomMove
7424          * Whether the component can move the Dom node when rendering (defaults to true).
7425          */
7426     allowDomMove : true,
7427     /** @cfg {String} hideMode
7428      * How this component should hidden. Supported values are
7429      * "visibility" (css visibility), "offsets" (negative offset position) and
7430      * "display" (css display) - defaults to "display".
7431      */
7432     hideMode: 'display',
7433
7434     /** @private */
7435     ctype : "Roo.Component",
7436
7437     /**
7438      * @cfg {String} actionMode 
7439      * which property holds the element that used for  hide() / show() / disable() / enable()
7440      * default is 'el' 
7441      */
7442     actionMode : "el",
7443
7444     /** @private */
7445     getActionEl : function(){
7446         return this[this.actionMode];
7447     },
7448
7449     initComponent : Roo.emptyFn,
7450     /**
7451      * If this is a lazy rendering component, render it to its container element.
7452      * @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.
7453      */
7454     render : function(container, position){
7455         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7456             if(!container && this.el){
7457                 this.el = Roo.get(this.el);
7458                 container = this.el.dom.parentNode;
7459                 this.allowDomMove = false;
7460             }
7461             this.container = Roo.get(container);
7462             this.rendered = true;
7463             if(position !== undefined){
7464                 if(typeof position == 'number'){
7465                     position = this.container.dom.childNodes[position];
7466                 }else{
7467                     position = Roo.getDom(position);
7468                 }
7469             }
7470             this.onRender(this.container, position || null);
7471             if(this.cls){
7472                 this.el.addClass(this.cls);
7473                 delete this.cls;
7474             }
7475             if(this.style){
7476                 this.el.applyStyles(this.style);
7477                 delete this.style;
7478             }
7479             this.fireEvent("render", this);
7480             this.afterRender(this.container);
7481             if(this.hidden){
7482                 this.hide();
7483             }
7484             if(this.disabled){
7485                 this.disable();
7486             }
7487         }
7488         return this;
7489     },
7490
7491     /** @private */
7492     // default function is not really useful
7493     onRender : function(ct, position){
7494         if(this.el){
7495             this.el = Roo.get(this.el);
7496             if(this.allowDomMove !== false){
7497                 ct.dom.insertBefore(this.el.dom, position);
7498             }
7499         }
7500     },
7501
7502     /** @private */
7503     getAutoCreate : function(){
7504         var cfg = typeof this.autoCreate == "object" ?
7505                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7506         if(this.id && !cfg.id){
7507             cfg.id = this.id;
7508         }
7509         return cfg;
7510     },
7511
7512     /** @private */
7513     afterRender : Roo.emptyFn,
7514
7515     /**
7516      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7517      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7518      */
7519     destroy : function(){
7520         if(this.fireEvent("beforedestroy", this) !== false){
7521             this.purgeListeners();
7522             this.beforeDestroy();
7523             if(this.rendered){
7524                 this.el.removeAllListeners();
7525                 this.el.remove();
7526                 if(this.actionMode == "container"){
7527                     this.container.remove();
7528                 }
7529             }
7530             this.onDestroy();
7531             Roo.ComponentMgr.unregister(this);
7532             this.fireEvent("destroy", this);
7533         }
7534     },
7535
7536         /** @private */
7537     beforeDestroy : function(){
7538
7539     },
7540
7541         /** @private */
7542         onDestroy : function(){
7543
7544     },
7545
7546     /**
7547      * Returns the underlying {@link Roo.Element}.
7548      * @return {Roo.Element} The element
7549      */
7550     getEl : function(){
7551         return this.el;
7552     },
7553
7554     /**
7555      * Returns the id of this component.
7556      * @return {String}
7557      */
7558     getId : function(){
7559         return this.id;
7560     },
7561
7562     /**
7563      * Try to focus this component.
7564      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7565      * @return {Roo.Component} this
7566      */
7567     focus : function(selectText){
7568         if(this.rendered){
7569             this.el.focus();
7570             if(selectText === true){
7571                 this.el.dom.select();
7572             }
7573         }
7574         return this;
7575     },
7576
7577     /** @private */
7578     blur : function(){
7579         if(this.rendered){
7580             this.el.blur();
7581         }
7582         return this;
7583     },
7584
7585     /**
7586      * Disable this component.
7587      * @return {Roo.Component} this
7588      */
7589     disable : function(){
7590         if(this.rendered){
7591             this.onDisable();
7592         }
7593         this.disabled = true;
7594         this.fireEvent("disable", this);
7595         return this;
7596     },
7597
7598         // private
7599     onDisable : function(){
7600         this.getActionEl().addClass(this.disabledClass);
7601         this.el.dom.disabled = true;
7602     },
7603
7604     /**
7605      * Enable this component.
7606      * @return {Roo.Component} this
7607      */
7608     enable : function(){
7609         if(this.rendered){
7610             this.onEnable();
7611         }
7612         this.disabled = false;
7613         this.fireEvent("enable", this);
7614         return this;
7615     },
7616
7617         // private
7618     onEnable : function(){
7619         this.getActionEl().removeClass(this.disabledClass);
7620         this.el.dom.disabled = false;
7621     },
7622
7623     /**
7624      * Convenience function for setting disabled/enabled by boolean.
7625      * @param {Boolean} disabled
7626      */
7627     setDisabled : function(disabled){
7628         this[disabled ? "disable" : "enable"]();
7629     },
7630
7631     /**
7632      * Show this component.
7633      * @return {Roo.Component} this
7634      */
7635     show: function(){
7636         if(this.fireEvent("beforeshow", this) !== false){
7637             this.hidden = false;
7638             if(this.rendered){
7639                 this.onShow();
7640             }
7641             this.fireEvent("show", this);
7642         }
7643         return this;
7644     },
7645
7646     // private
7647     onShow : function(){
7648         var ae = this.getActionEl();
7649         if(this.hideMode == 'visibility'){
7650             ae.dom.style.visibility = "visible";
7651         }else if(this.hideMode == 'offsets'){
7652             ae.removeClass('x-hidden');
7653         }else{
7654             ae.dom.style.display = "";
7655         }
7656     },
7657
7658     /**
7659      * Hide this component.
7660      * @return {Roo.Component} this
7661      */
7662     hide: function(){
7663         if(this.fireEvent("beforehide", this) !== false){
7664             this.hidden = true;
7665             if(this.rendered){
7666                 this.onHide();
7667             }
7668             this.fireEvent("hide", this);
7669         }
7670         return this;
7671     },
7672
7673     // private
7674     onHide : function(){
7675         var ae = this.getActionEl();
7676         if(this.hideMode == 'visibility'){
7677             ae.dom.style.visibility = "hidden";
7678         }else if(this.hideMode == 'offsets'){
7679             ae.addClass('x-hidden');
7680         }else{
7681             ae.dom.style.display = "none";
7682         }
7683     },
7684
7685     /**
7686      * Convenience function to hide or show this component by boolean.
7687      * @param {Boolean} visible True to show, false to hide
7688      * @return {Roo.Component} this
7689      */
7690     setVisible: function(visible){
7691         if(visible) {
7692             this.show();
7693         }else{
7694             this.hide();
7695         }
7696         return this;
7697     },
7698
7699     /**
7700      * Returns true if this component is visible.
7701      */
7702     isVisible : function(){
7703         return this.getActionEl().isVisible();
7704     },
7705
7706     cloneConfig : function(overrides){
7707         overrides = overrides || {};
7708         var id = overrides.id || Roo.id();
7709         var cfg = Roo.applyIf(overrides, this.initialConfig);
7710         cfg.id = id; // prevent dup id
7711         return new this.constructor(cfg);
7712     }
7713 });/*
7714  * Based on:
7715  * Ext JS Library 1.1.1
7716  * Copyright(c) 2006-2007, Ext JS, LLC.
7717  *
7718  * Originally Released Under LGPL - original licence link has changed is not relivant.
7719  *
7720  * Fork - LGPL
7721  * <script type="text/javascript">
7722  */
7723  (function(){ 
7724 /**
7725  * @class Roo.Layer
7726  * @extends Roo.Element
7727  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7728  * automatic maintaining of shadow/shim positions.
7729  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7730  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7731  * you can pass a string with a CSS class name. False turns off the shadow.
7732  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7733  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7734  * @cfg {String} cls CSS class to add to the element
7735  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7736  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7737  * @constructor
7738  * @param {Object} config An object with config options.
7739  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7740  */
7741
7742 Roo.Layer = function(config, existingEl){
7743     config = config || {};
7744     var dh = Roo.DomHelper;
7745     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7746     if(existingEl){
7747         this.dom = Roo.getDom(existingEl);
7748     }
7749     if(!this.dom){
7750         var o = config.dh || {tag: "div", cls: "x-layer"};
7751         this.dom = dh.append(pel, o);
7752     }
7753     if(config.cls){
7754         this.addClass(config.cls);
7755     }
7756     this.constrain = config.constrain !== false;
7757     this.visibilityMode = Roo.Element.VISIBILITY;
7758     if(config.id){
7759         this.id = this.dom.id = config.id;
7760     }else{
7761         this.id = Roo.id(this.dom);
7762     }
7763     this.zindex = config.zindex || this.getZIndex();
7764     this.position("absolute", this.zindex);
7765     if(config.shadow){
7766         this.shadowOffset = config.shadowOffset || 4;
7767         this.shadow = new Roo.Shadow({
7768             offset : this.shadowOffset,
7769             mode : config.shadow
7770         });
7771     }else{
7772         this.shadowOffset = 0;
7773     }
7774     this.useShim = config.shim !== false && Roo.useShims;
7775     this.useDisplay = config.useDisplay;
7776     this.hide();
7777 };
7778
7779 var supr = Roo.Element.prototype;
7780
7781 // shims are shared among layer to keep from having 100 iframes
7782 var shims = [];
7783
7784 Roo.extend(Roo.Layer, Roo.Element, {
7785
7786     getZIndex : function(){
7787         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7788     },
7789
7790     getShim : function(){
7791         if(!this.useShim){
7792             return null;
7793         }
7794         if(this.shim){
7795             return this.shim;
7796         }
7797         var shim = shims.shift();
7798         if(!shim){
7799             shim = this.createShim();
7800             shim.enableDisplayMode('block');
7801             shim.dom.style.display = 'none';
7802             shim.dom.style.visibility = 'visible';
7803         }
7804         var pn = this.dom.parentNode;
7805         if(shim.dom.parentNode != pn){
7806             pn.insertBefore(shim.dom, this.dom);
7807         }
7808         shim.setStyle('z-index', this.getZIndex()-2);
7809         this.shim = shim;
7810         return shim;
7811     },
7812
7813     hideShim : function(){
7814         if(this.shim){
7815             this.shim.setDisplayed(false);
7816             shims.push(this.shim);
7817             delete this.shim;
7818         }
7819     },
7820
7821     disableShadow : function(){
7822         if(this.shadow){
7823             this.shadowDisabled = true;
7824             this.shadow.hide();
7825             this.lastShadowOffset = this.shadowOffset;
7826             this.shadowOffset = 0;
7827         }
7828     },
7829
7830     enableShadow : function(show){
7831         if(this.shadow){
7832             this.shadowDisabled = false;
7833             this.shadowOffset = this.lastShadowOffset;
7834             delete this.lastShadowOffset;
7835             if(show){
7836                 this.sync(true);
7837             }
7838         }
7839     },
7840
7841     // private
7842     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7843     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7844     sync : function(doShow){
7845         var sw = this.shadow;
7846         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7847             var sh = this.getShim();
7848
7849             var w = this.getWidth(),
7850                 h = this.getHeight();
7851
7852             var l = this.getLeft(true),
7853                 t = this.getTop(true);
7854
7855             if(sw && !this.shadowDisabled){
7856                 if(doShow && !sw.isVisible()){
7857                     sw.show(this);
7858                 }else{
7859                     sw.realign(l, t, w, h);
7860                 }
7861                 if(sh){
7862                     if(doShow){
7863                        sh.show();
7864                     }
7865                     // fit the shim behind the shadow, so it is shimmed too
7866                     var a = sw.adjusts, s = sh.dom.style;
7867                     s.left = (Math.min(l, l+a.l))+"px";
7868                     s.top = (Math.min(t, t+a.t))+"px";
7869                     s.width = (w+a.w)+"px";
7870                     s.height = (h+a.h)+"px";
7871                 }
7872             }else if(sh){
7873                 if(doShow){
7874                    sh.show();
7875                 }
7876                 sh.setSize(w, h);
7877                 sh.setLeftTop(l, t);
7878             }
7879             
7880         }
7881     },
7882
7883     // private
7884     destroy : function(){
7885         this.hideShim();
7886         if(this.shadow){
7887             this.shadow.hide();
7888         }
7889         this.removeAllListeners();
7890         var pn = this.dom.parentNode;
7891         if(pn){
7892             pn.removeChild(this.dom);
7893         }
7894         Roo.Element.uncache(this.id);
7895     },
7896
7897     remove : function(){
7898         this.destroy();
7899     },
7900
7901     // private
7902     beginUpdate : function(){
7903         this.updating = true;
7904     },
7905
7906     // private
7907     endUpdate : function(){
7908         this.updating = false;
7909         this.sync(true);
7910     },
7911
7912     // private
7913     hideUnders : function(negOffset){
7914         if(this.shadow){
7915             this.shadow.hide();
7916         }
7917         this.hideShim();
7918     },
7919
7920     // private
7921     constrainXY : function(){
7922         if(this.constrain){
7923             var vw = Roo.lib.Dom.getViewWidth(),
7924                 vh = Roo.lib.Dom.getViewHeight();
7925             var s = Roo.get(document).getScroll();
7926
7927             var xy = this.getXY();
7928             var x = xy[0], y = xy[1];   
7929             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7930             // only move it if it needs it
7931             var moved = false;
7932             // first validate right/bottom
7933             if((x + w) > vw+s.left){
7934                 x = vw - w - this.shadowOffset;
7935                 moved = true;
7936             }
7937             if((y + h) > vh+s.top){
7938                 y = vh - h - this.shadowOffset;
7939                 moved = true;
7940             }
7941             // then make sure top/left isn't negative
7942             if(x < s.left){
7943                 x = s.left;
7944                 moved = true;
7945             }
7946             if(y < s.top){
7947                 y = s.top;
7948                 moved = true;
7949             }
7950             if(moved){
7951                 if(this.avoidY){
7952                     var ay = this.avoidY;
7953                     if(y <= ay && (y+h) >= ay){
7954                         y = ay-h-5;   
7955                     }
7956                 }
7957                 xy = [x, y];
7958                 this.storeXY(xy);
7959                 supr.setXY.call(this, xy);
7960                 this.sync();
7961             }
7962         }
7963     },
7964
7965     isVisible : function(){
7966         return this.visible;    
7967     },
7968
7969     // private
7970     showAction : function(){
7971         this.visible = true; // track visibility to prevent getStyle calls
7972         if(this.useDisplay === true){
7973             this.setDisplayed("");
7974         }else if(this.lastXY){
7975             supr.setXY.call(this, this.lastXY);
7976         }else if(this.lastLT){
7977             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7978         }
7979     },
7980
7981     // private
7982     hideAction : function(){
7983         this.visible = false;
7984         if(this.useDisplay === true){
7985             this.setDisplayed(false);
7986         }else{
7987             this.setLeftTop(-10000,-10000);
7988         }
7989     },
7990
7991     // overridden Element method
7992     setVisible : function(v, a, d, c, e){
7993         if(v){
7994             this.showAction();
7995         }
7996         if(a && v){
7997             var cb = function(){
7998                 this.sync(true);
7999                 if(c){
8000                     c();
8001                 }
8002             }.createDelegate(this);
8003             supr.setVisible.call(this, true, true, d, cb, e);
8004         }else{
8005             if(!v){
8006                 this.hideUnders(true);
8007             }
8008             var cb = c;
8009             if(a){
8010                 cb = function(){
8011                     this.hideAction();
8012                     if(c){
8013                         c();
8014                     }
8015                 }.createDelegate(this);
8016             }
8017             supr.setVisible.call(this, v, a, d, cb, e);
8018             if(v){
8019                 this.sync(true);
8020             }else if(!a){
8021                 this.hideAction();
8022             }
8023         }
8024     },
8025
8026     storeXY : function(xy){
8027         delete this.lastLT;
8028         this.lastXY = xy;
8029     },
8030
8031     storeLeftTop : function(left, top){
8032         delete this.lastXY;
8033         this.lastLT = [left, top];
8034     },
8035
8036     // private
8037     beforeFx : function(){
8038         this.beforeAction();
8039         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
8040     },
8041
8042     // private
8043     afterFx : function(){
8044         Roo.Layer.superclass.afterFx.apply(this, arguments);
8045         this.sync(this.isVisible());
8046     },
8047
8048     // private
8049     beforeAction : function(){
8050         if(!this.updating && this.shadow){
8051             this.shadow.hide();
8052         }
8053     },
8054
8055     // overridden Element method
8056     setLeft : function(left){
8057         this.storeLeftTop(left, this.getTop(true));
8058         supr.setLeft.apply(this, arguments);
8059         this.sync();
8060     },
8061
8062     setTop : function(top){
8063         this.storeLeftTop(this.getLeft(true), top);
8064         supr.setTop.apply(this, arguments);
8065         this.sync();
8066     },
8067
8068     setLeftTop : function(left, top){
8069         this.storeLeftTop(left, top);
8070         supr.setLeftTop.apply(this, arguments);
8071         this.sync();
8072     },
8073
8074     setXY : function(xy, a, d, c, e){
8075         this.fixDisplay();
8076         this.beforeAction();
8077         this.storeXY(xy);
8078         var cb = this.createCB(c);
8079         supr.setXY.call(this, xy, a, d, cb, e);
8080         if(!a){
8081             cb();
8082         }
8083     },
8084
8085     // private
8086     createCB : function(c){
8087         var el = this;
8088         return function(){
8089             el.constrainXY();
8090             el.sync(true);
8091             if(c){
8092                 c();
8093             }
8094         };
8095     },
8096
8097     // overridden Element method
8098     setX : function(x, a, d, c, e){
8099         this.setXY([x, this.getY()], a, d, c, e);
8100     },
8101
8102     // overridden Element method
8103     setY : function(y, a, d, c, e){
8104         this.setXY([this.getX(), y], a, d, c, e);
8105     },
8106
8107     // overridden Element method
8108     setSize : function(w, h, a, d, c, e){
8109         this.beforeAction();
8110         var cb = this.createCB(c);
8111         supr.setSize.call(this, w, h, a, d, cb, e);
8112         if(!a){
8113             cb();
8114         }
8115     },
8116
8117     // overridden Element method
8118     setWidth : function(w, a, d, c, e){
8119         this.beforeAction();
8120         var cb = this.createCB(c);
8121         supr.setWidth.call(this, w, a, d, cb, e);
8122         if(!a){
8123             cb();
8124         }
8125     },
8126
8127     // overridden Element method
8128     setHeight : function(h, a, d, c, e){
8129         this.beforeAction();
8130         var cb = this.createCB(c);
8131         supr.setHeight.call(this, h, a, d, cb, e);
8132         if(!a){
8133             cb();
8134         }
8135     },
8136
8137     // overridden Element method
8138     setBounds : function(x, y, w, h, a, d, c, e){
8139         this.beforeAction();
8140         var cb = this.createCB(c);
8141         if(!a){
8142             this.storeXY([x, y]);
8143             supr.setXY.call(this, [x, y]);
8144             supr.setSize.call(this, w, h, a, d, cb, e);
8145             cb();
8146         }else{
8147             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8148         }
8149         return this;
8150     },
8151     
8152     /**
8153      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8154      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8155      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8156      * @param {Number} zindex The new z-index to set
8157      * @return {this} The Layer
8158      */
8159     setZIndex : function(zindex){
8160         this.zindex = zindex;
8161         this.setStyle("z-index", zindex + 2);
8162         if(this.shadow){
8163             this.shadow.setZIndex(zindex + 1);
8164         }
8165         if(this.shim){
8166             this.shim.setStyle("z-index", zindex);
8167         }
8168     }
8169 });
8170 })();/*
8171  * Based on:
8172  * Ext JS Library 1.1.1
8173  * Copyright(c) 2006-2007, Ext JS, LLC.
8174  *
8175  * Originally Released Under LGPL - original licence link has changed is not relivant.
8176  *
8177  * Fork - LGPL
8178  * <script type="text/javascript">
8179  */
8180
8181
8182 /**
8183  * @class Roo.Shadow
8184  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8185  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8186  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8187  * @constructor
8188  * Create a new Shadow
8189  * @param {Object} config The config object
8190  */
8191 Roo.Shadow = function(config){
8192     Roo.apply(this, config);
8193     if(typeof this.mode != "string"){
8194         this.mode = this.defaultMode;
8195     }
8196     var o = this.offset, a = {h: 0};
8197     var rad = Math.floor(this.offset/2);
8198     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8199         case "drop":
8200             a.w = 0;
8201             a.l = a.t = o;
8202             a.t -= 1;
8203             if(Roo.isIE){
8204                 a.l -= this.offset + rad;
8205                 a.t -= this.offset + rad;
8206                 a.w -= rad;
8207                 a.h -= rad;
8208                 a.t += 1;
8209             }
8210         break;
8211         case "sides":
8212             a.w = (o*2);
8213             a.l = -o;
8214             a.t = o-1;
8215             if(Roo.isIE){
8216                 a.l -= (this.offset - rad);
8217                 a.t -= this.offset + rad;
8218                 a.l += 1;
8219                 a.w -= (this.offset - rad)*2;
8220                 a.w -= rad + 1;
8221                 a.h -= 1;
8222             }
8223         break;
8224         case "frame":
8225             a.w = a.h = (o*2);
8226             a.l = a.t = -o;
8227             a.t += 1;
8228             a.h -= 2;
8229             if(Roo.isIE){
8230                 a.l -= (this.offset - rad);
8231                 a.t -= (this.offset - rad);
8232                 a.l += 1;
8233                 a.w -= (this.offset + rad + 1);
8234                 a.h -= (this.offset + rad);
8235                 a.h += 1;
8236             }
8237         break;
8238     };
8239
8240     this.adjusts = a;
8241 };
8242
8243 Roo.Shadow.prototype = {
8244     /**
8245      * @cfg {String} mode
8246      * The shadow display mode.  Supports the following options:<br />
8247      * sides: Shadow displays on both sides and bottom only<br />
8248      * frame: Shadow displays equally on all four sides<br />
8249      * drop: Traditional bottom-right drop shadow (default)
8250      */
8251     /**
8252      * @cfg {String} offset
8253      * The number of pixels to offset the shadow from the element (defaults to 4)
8254      */
8255     offset: 4,
8256
8257     // private
8258     defaultMode: "drop",
8259
8260     /**
8261      * Displays the shadow under the target element
8262      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8263      */
8264     show : function(target){
8265         target = Roo.get(target);
8266         if(!this.el){
8267             this.el = Roo.Shadow.Pool.pull();
8268             if(this.el.dom.nextSibling != target.dom){
8269                 this.el.insertBefore(target);
8270             }
8271         }
8272         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8273         if(Roo.isIE){
8274             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8275         }
8276         this.realign(
8277             target.getLeft(true),
8278             target.getTop(true),
8279             target.getWidth(),
8280             target.getHeight()
8281         );
8282         this.el.dom.style.display = "block";
8283     },
8284
8285     /**
8286      * Returns true if the shadow is visible, else false
8287      */
8288     isVisible : function(){
8289         return this.el ? true : false;  
8290     },
8291
8292     /**
8293      * Direct alignment when values are already available. Show must be called at least once before
8294      * calling this method to ensure it is initialized.
8295      * @param {Number} left The target element left position
8296      * @param {Number} top The target element top position
8297      * @param {Number} width The target element width
8298      * @param {Number} height The target element height
8299      */
8300     realign : function(l, t, w, h){
8301         if(!this.el){
8302             return;
8303         }
8304         var a = this.adjusts, d = this.el.dom, s = d.style;
8305         var iea = 0;
8306         s.left = (l+a.l)+"px";
8307         s.top = (t+a.t)+"px";
8308         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8309  
8310         if(s.width != sws || s.height != shs){
8311             s.width = sws;
8312             s.height = shs;
8313             if(!Roo.isIE){
8314                 var cn = d.childNodes;
8315                 var sww = Math.max(0, (sw-12))+"px";
8316                 cn[0].childNodes[1].style.width = sww;
8317                 cn[1].childNodes[1].style.width = sww;
8318                 cn[2].childNodes[1].style.width = sww;
8319                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8320             }
8321         }
8322     },
8323
8324     /**
8325      * Hides this shadow
8326      */
8327     hide : function(){
8328         if(this.el){
8329             this.el.dom.style.display = "none";
8330             Roo.Shadow.Pool.push(this.el);
8331             delete this.el;
8332         }
8333     },
8334
8335     /**
8336      * Adjust the z-index of this shadow
8337      * @param {Number} zindex The new z-index
8338      */
8339     setZIndex : function(z){
8340         this.zIndex = z;
8341         if(this.el){
8342             this.el.setStyle("z-index", z);
8343         }
8344     }
8345 };
8346
8347 // Private utility class that manages the internal Shadow cache
8348 Roo.Shadow.Pool = function(){
8349     var p = [];
8350     var markup = Roo.isIE ?
8351                  '<div class="x-ie-shadow"></div>' :
8352                  '<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>';
8353     return {
8354         pull : function(){
8355             var sh = p.shift();
8356             if(!sh){
8357                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8358                 sh.autoBoxAdjust = false;
8359             }
8360             return sh;
8361         },
8362
8363         push : function(sh){
8364             p.push(sh);
8365         }
8366     };
8367 }();/*
8368  * Based on:
8369  * Ext JS Library 1.1.1
8370  * Copyright(c) 2006-2007, Ext JS, LLC.
8371  *
8372  * Originally Released Under LGPL - original licence link has changed is not relivant.
8373  *
8374  * Fork - LGPL
8375  * <script type="text/javascript">
8376  */
8377
8378 /**
8379  * @class Roo.BoxComponent
8380  * @extends Roo.Component
8381  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8382  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8383  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8384  * layout containers.
8385  * @constructor
8386  * @param {Roo.Element/String/Object} config The configuration options.
8387  */
8388 Roo.BoxComponent = function(config){
8389     Roo.Component.call(this, config);
8390     this.addEvents({
8391         /**
8392          * @event resize
8393          * Fires after the component is resized.
8394              * @param {Roo.Component} this
8395              * @param {Number} adjWidth The box-adjusted width that was set
8396              * @param {Number} adjHeight The box-adjusted height that was set
8397              * @param {Number} rawWidth The width that was originally specified
8398              * @param {Number} rawHeight The height that was originally specified
8399              */
8400         resize : true,
8401         /**
8402          * @event move
8403          * Fires after the component is moved.
8404              * @param {Roo.Component} this
8405              * @param {Number} x The new x position
8406              * @param {Number} y The new y position
8407              */
8408         move : true
8409     });
8410 };
8411
8412 Roo.extend(Roo.BoxComponent, Roo.Component, {
8413     // private, set in afterRender to signify that the component has been rendered
8414     boxReady : false,
8415     // private, used to defer height settings to subclasses
8416     deferHeight: false,
8417     /** @cfg {Number} width
8418      * width (optional) size of component
8419      */
8420      /** @cfg {Number} height
8421      * height (optional) size of component
8422      */
8423      
8424     /**
8425      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8426      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8427      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8428      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8429      * @return {Roo.BoxComponent} this
8430      */
8431     setSize : function(w, h){
8432         // support for standard size objects
8433         if(typeof w == 'object'){
8434             h = w.height;
8435             w = w.width;
8436         }
8437         // not rendered
8438         if(!this.boxReady){
8439             this.width = w;
8440             this.height = h;
8441             return this;
8442         }
8443
8444         // prevent recalcs when not needed
8445         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8446             return this;
8447         }
8448         this.lastSize = {width: w, height: h};
8449
8450         var adj = this.adjustSize(w, h);
8451         var aw = adj.width, ah = adj.height;
8452         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8453             var rz = this.getResizeEl();
8454             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8455                 rz.setSize(aw, ah);
8456             }else if(!this.deferHeight && ah !== undefined){
8457                 rz.setHeight(ah);
8458             }else if(aw !== undefined){
8459                 rz.setWidth(aw);
8460             }
8461             this.onResize(aw, ah, w, h);
8462             this.fireEvent('resize', this, aw, ah, w, h);
8463         }
8464         return this;
8465     },
8466
8467     /**
8468      * Gets the current size of the component's underlying element.
8469      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8470      */
8471     getSize : function(){
8472         return this.el.getSize();
8473     },
8474
8475     /**
8476      * Gets the current XY position of the component's underlying element.
8477      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8478      * @return {Array} The XY position of the element (e.g., [100, 200])
8479      */
8480     getPosition : function(local){
8481         if(local === true){
8482             return [this.el.getLeft(true), this.el.getTop(true)];
8483         }
8484         return this.xy || this.el.getXY();
8485     },
8486
8487     /**
8488      * Gets the current box measurements of the component's underlying element.
8489      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8490      * @returns {Object} box An object in the format {x, y, width, height}
8491      */
8492     getBox : function(local){
8493         var s = this.el.getSize();
8494         if(local){
8495             s.x = this.el.getLeft(true);
8496             s.y = this.el.getTop(true);
8497         }else{
8498             var xy = this.xy || this.el.getXY();
8499             s.x = xy[0];
8500             s.y = xy[1];
8501         }
8502         return s;
8503     },
8504
8505     /**
8506      * Sets the current box measurements of the component's underlying element.
8507      * @param {Object} box An object in the format {x, y, width, height}
8508      * @returns {Roo.BoxComponent} this
8509      */
8510     updateBox : function(box){
8511         this.setSize(box.width, box.height);
8512         this.setPagePosition(box.x, box.y);
8513         return this;
8514     },
8515
8516     // protected
8517     getResizeEl : function(){
8518         return this.resizeEl || this.el;
8519     },
8520
8521     // protected
8522     getPositionEl : function(){
8523         return this.positionEl || this.el;
8524     },
8525
8526     /**
8527      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8528      * This method fires the move event.
8529      * @param {Number} left The new left
8530      * @param {Number} top The new top
8531      * @returns {Roo.BoxComponent} this
8532      */
8533     setPosition : function(x, y){
8534         this.x = x;
8535         this.y = y;
8536         if(!this.boxReady){
8537             return this;
8538         }
8539         var adj = this.adjustPosition(x, y);
8540         var ax = adj.x, ay = adj.y;
8541
8542         var el = this.getPositionEl();
8543         if(ax !== undefined || ay !== undefined){
8544             if(ax !== undefined && ay !== undefined){
8545                 el.setLeftTop(ax, ay);
8546             }else if(ax !== undefined){
8547                 el.setLeft(ax);
8548             }else if(ay !== undefined){
8549                 el.setTop(ay);
8550             }
8551             this.onPosition(ax, ay);
8552             this.fireEvent('move', this, ax, ay);
8553         }
8554         return this;
8555     },
8556
8557     /**
8558      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8559      * This method fires the move event.
8560      * @param {Number} x The new x position
8561      * @param {Number} y The new y position
8562      * @returns {Roo.BoxComponent} this
8563      */
8564     setPagePosition : function(x, y){
8565         this.pageX = x;
8566         this.pageY = y;
8567         if(!this.boxReady){
8568             return;
8569         }
8570         if(x === undefined || y === undefined){ // cannot translate undefined points
8571             return;
8572         }
8573         var p = this.el.translatePoints(x, y);
8574         this.setPosition(p.left, p.top);
8575         return this;
8576     },
8577
8578     // private
8579     onRender : function(ct, position){
8580         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8581         if(this.resizeEl){
8582             this.resizeEl = Roo.get(this.resizeEl);
8583         }
8584         if(this.positionEl){
8585             this.positionEl = Roo.get(this.positionEl);
8586         }
8587     },
8588
8589     // private
8590     afterRender : function(){
8591         Roo.BoxComponent.superclass.afterRender.call(this);
8592         this.boxReady = true;
8593         this.setSize(this.width, this.height);
8594         if(this.x || this.y){
8595             this.setPosition(this.x, this.y);
8596         }
8597         if(this.pageX || this.pageY){
8598             this.setPagePosition(this.pageX, this.pageY);
8599         }
8600     },
8601
8602     /**
8603      * Force the component's size to recalculate based on the underlying element's current height and width.
8604      * @returns {Roo.BoxComponent} this
8605      */
8606     syncSize : function(){
8607         delete this.lastSize;
8608         this.setSize(this.el.getWidth(), this.el.getHeight());
8609         return this;
8610     },
8611
8612     /**
8613      * Called after the component is resized, this method is empty by default but can be implemented by any
8614      * subclass that needs to perform custom logic after a resize occurs.
8615      * @param {Number} adjWidth The box-adjusted width that was set
8616      * @param {Number} adjHeight The box-adjusted height that was set
8617      * @param {Number} rawWidth The width that was originally specified
8618      * @param {Number} rawHeight The height that was originally specified
8619      */
8620     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8621
8622     },
8623
8624     /**
8625      * Called after the component is moved, this method is empty by default but can be implemented by any
8626      * subclass that needs to perform custom logic after a move occurs.
8627      * @param {Number} x The new x position
8628      * @param {Number} y The new y position
8629      */
8630     onPosition : function(x, y){
8631
8632     },
8633
8634     // private
8635     adjustSize : function(w, h){
8636         if(this.autoWidth){
8637             w = 'auto';
8638         }
8639         if(this.autoHeight){
8640             h = 'auto';
8641         }
8642         return {width : w, height: h};
8643     },
8644
8645     // private
8646     adjustPosition : function(x, y){
8647         return {x : x, y: y};
8648     }
8649 });/*
8650  * Based on:
8651  * Ext JS Library 1.1.1
8652  * Copyright(c) 2006-2007, Ext JS, LLC.
8653  *
8654  * Originally Released Under LGPL - original licence link has changed is not relivant.
8655  *
8656  * Fork - LGPL
8657  * <script type="text/javascript">
8658  */
8659
8660
8661 /**
8662  * @class Roo.SplitBar
8663  * @extends Roo.util.Observable
8664  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8665  * <br><br>
8666  * Usage:
8667  * <pre><code>
8668 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8669                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8670 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8671 split.minSize = 100;
8672 split.maxSize = 600;
8673 split.animate = true;
8674 split.on('moved', splitterMoved);
8675 </code></pre>
8676  * @constructor
8677  * Create a new SplitBar
8678  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8679  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8680  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8681  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8682                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8683                         position of the SplitBar).
8684  */
8685 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8686     
8687     /** @private */
8688     this.el = Roo.get(dragElement, true);
8689     this.el.dom.unselectable = "on";
8690     /** @private */
8691     this.resizingEl = Roo.get(resizingElement, true);
8692
8693     /**
8694      * @private
8695      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8696      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8697      * @type Number
8698      */
8699     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8700     
8701     /**
8702      * The minimum size of the resizing element. (Defaults to 0)
8703      * @type Number
8704      */
8705     this.minSize = 0;
8706     
8707     /**
8708      * The maximum size of the resizing element. (Defaults to 2000)
8709      * @type Number
8710      */
8711     this.maxSize = 2000;
8712     
8713     /**
8714      * Whether to animate the transition to the new size
8715      * @type Boolean
8716      */
8717     this.animate = false;
8718     
8719     /**
8720      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8721      * @type Boolean
8722      */
8723     this.useShim = false;
8724     
8725     /** @private */
8726     this.shim = null;
8727     
8728     if(!existingProxy){
8729         /** @private */
8730         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8731     }else{
8732         this.proxy = Roo.get(existingProxy).dom;
8733     }
8734     /** @private */
8735     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8736     
8737     /** @private */
8738     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8739     
8740     /** @private */
8741     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8742     
8743     /** @private */
8744     this.dragSpecs = {};
8745     
8746     /**
8747      * @private The adapter to use to positon and resize elements
8748      */
8749     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8750     this.adapter.init(this);
8751     
8752     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8753         /** @private */
8754         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8755         this.el.addClass("x-splitbar-h");
8756     }else{
8757         /** @private */
8758         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8759         this.el.addClass("x-splitbar-v");
8760     }
8761     
8762     this.addEvents({
8763         /**
8764          * @event resize
8765          * Fires when the splitter is moved (alias for {@link #event-moved})
8766          * @param {Roo.SplitBar} this
8767          * @param {Number} newSize the new width or height
8768          */
8769         "resize" : true,
8770         /**
8771          * @event moved
8772          * Fires when the splitter is moved
8773          * @param {Roo.SplitBar} this
8774          * @param {Number} newSize the new width or height
8775          */
8776         "moved" : true,
8777         /**
8778          * @event beforeresize
8779          * Fires before the splitter is dragged
8780          * @param {Roo.SplitBar} this
8781          */
8782         "beforeresize" : true,
8783
8784         "beforeapply" : true
8785     });
8786
8787     Roo.util.Observable.call(this);
8788 };
8789
8790 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8791     onStartProxyDrag : function(x, y){
8792         this.fireEvent("beforeresize", this);
8793         if(!this.overlay){
8794             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8795             o.unselectable();
8796             o.enableDisplayMode("block");
8797             // all splitbars share the same overlay
8798             Roo.SplitBar.prototype.overlay = o;
8799         }
8800         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8801         this.overlay.show();
8802         Roo.get(this.proxy).setDisplayed("block");
8803         var size = this.adapter.getElementSize(this);
8804         this.activeMinSize = this.getMinimumSize();;
8805         this.activeMaxSize = this.getMaximumSize();;
8806         var c1 = size - this.activeMinSize;
8807         var c2 = Math.max(this.activeMaxSize - size, 0);
8808         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8809             this.dd.resetConstraints();
8810             this.dd.setXConstraint(
8811                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8812                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8813             );
8814             this.dd.setYConstraint(0, 0);
8815         }else{
8816             this.dd.resetConstraints();
8817             this.dd.setXConstraint(0, 0);
8818             this.dd.setYConstraint(
8819                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8820                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8821             );
8822          }
8823         this.dragSpecs.startSize = size;
8824         this.dragSpecs.startPoint = [x, y];
8825         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8826     },
8827     
8828     /** 
8829      * @private Called after the drag operation by the DDProxy
8830      */
8831     onEndProxyDrag : function(e){
8832         Roo.get(this.proxy).setDisplayed(false);
8833         var endPoint = Roo.lib.Event.getXY(e);
8834         if(this.overlay){
8835             this.overlay.hide();
8836         }
8837         var newSize;
8838         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8839             newSize = this.dragSpecs.startSize + 
8840                 (this.placement == Roo.SplitBar.LEFT ?
8841                     endPoint[0] - this.dragSpecs.startPoint[0] :
8842                     this.dragSpecs.startPoint[0] - endPoint[0]
8843                 );
8844         }else{
8845             newSize = this.dragSpecs.startSize + 
8846                 (this.placement == Roo.SplitBar.TOP ?
8847                     endPoint[1] - this.dragSpecs.startPoint[1] :
8848                     this.dragSpecs.startPoint[1] - endPoint[1]
8849                 );
8850         }
8851         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8852         if(newSize != this.dragSpecs.startSize){
8853             if(this.fireEvent('beforeapply', this, newSize) !== false){
8854                 this.adapter.setElementSize(this, newSize);
8855                 this.fireEvent("moved", this, newSize);
8856                 this.fireEvent("resize", this, newSize);
8857             }
8858         }
8859     },
8860     
8861     /**
8862      * Get the adapter this SplitBar uses
8863      * @return The adapter object
8864      */
8865     getAdapter : function(){
8866         return this.adapter;
8867     },
8868     
8869     /**
8870      * Set the adapter this SplitBar uses
8871      * @param {Object} adapter A SplitBar adapter object
8872      */
8873     setAdapter : function(adapter){
8874         this.adapter = adapter;
8875         this.adapter.init(this);
8876     },
8877     
8878     /**
8879      * Gets the minimum size for the resizing element
8880      * @return {Number} The minimum size
8881      */
8882     getMinimumSize : function(){
8883         return this.minSize;
8884     },
8885     
8886     /**
8887      * Sets the minimum size for the resizing element
8888      * @param {Number} minSize The minimum size
8889      */
8890     setMinimumSize : function(minSize){
8891         this.minSize = minSize;
8892     },
8893     
8894     /**
8895      * Gets the maximum size for the resizing element
8896      * @return {Number} The maximum size
8897      */
8898     getMaximumSize : function(){
8899         return this.maxSize;
8900     },
8901     
8902     /**
8903      * Sets the maximum size for the resizing element
8904      * @param {Number} maxSize The maximum size
8905      */
8906     setMaximumSize : function(maxSize){
8907         this.maxSize = maxSize;
8908     },
8909     
8910     /**
8911      * Sets the initialize size for the resizing element
8912      * @param {Number} size The initial size
8913      */
8914     setCurrentSize : function(size){
8915         var oldAnimate = this.animate;
8916         this.animate = false;
8917         this.adapter.setElementSize(this, size);
8918         this.animate = oldAnimate;
8919     },
8920     
8921     /**
8922      * Destroy this splitbar. 
8923      * @param {Boolean} removeEl True to remove the element
8924      */
8925     destroy : function(removeEl){
8926         if(this.shim){
8927             this.shim.remove();
8928         }
8929         this.dd.unreg();
8930         this.proxy.parentNode.removeChild(this.proxy);
8931         if(removeEl){
8932             this.el.remove();
8933         }
8934     }
8935 });
8936
8937 /**
8938  * @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.
8939  */
8940 Roo.SplitBar.createProxy = function(dir){
8941     var proxy = new Roo.Element(document.createElement("div"));
8942     proxy.unselectable();
8943     var cls = 'x-splitbar-proxy';
8944     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8945     document.body.appendChild(proxy.dom);
8946     return proxy.dom;
8947 };
8948
8949 /** 
8950  * @class Roo.SplitBar.BasicLayoutAdapter
8951  * Default Adapter. It assumes the splitter and resizing element are not positioned
8952  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8953  */
8954 Roo.SplitBar.BasicLayoutAdapter = function(){
8955 };
8956
8957 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8958     // do nothing for now
8959     init : function(s){
8960     
8961     },
8962     /**
8963      * Called before drag operations to get the current size of the resizing element. 
8964      * @param {Roo.SplitBar} s The SplitBar using this adapter
8965      */
8966      getElementSize : function(s){
8967         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8968             return s.resizingEl.getWidth();
8969         }else{
8970             return s.resizingEl.getHeight();
8971         }
8972     },
8973     
8974     /**
8975      * Called after drag operations to set the size of the resizing element.
8976      * @param {Roo.SplitBar} s The SplitBar using this adapter
8977      * @param {Number} newSize The new size to set
8978      * @param {Function} onComplete A function to be invoked when resizing is complete
8979      */
8980     setElementSize : function(s, newSize, onComplete){
8981         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8982             if(!s.animate){
8983                 s.resizingEl.setWidth(newSize);
8984                 if(onComplete){
8985                     onComplete(s, newSize);
8986                 }
8987             }else{
8988                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8989             }
8990         }else{
8991             
8992             if(!s.animate){
8993                 s.resizingEl.setHeight(newSize);
8994                 if(onComplete){
8995                     onComplete(s, newSize);
8996                 }
8997             }else{
8998                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
8999             }
9000         }
9001     }
9002 };
9003
9004 /** 
9005  *@class Roo.SplitBar.AbsoluteLayoutAdapter
9006  * @extends Roo.SplitBar.BasicLayoutAdapter
9007  * Adapter that  moves the splitter element to align with the resized sizing element. 
9008  * Used with an absolute positioned SplitBar.
9009  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
9010  * document.body, make sure you assign an id to the body element.
9011  */
9012 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
9013     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
9014     this.container = Roo.get(container);
9015 };
9016
9017 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
9018     init : function(s){
9019         this.basic.init(s);
9020     },
9021     
9022     getElementSize : function(s){
9023         return this.basic.getElementSize(s);
9024     },
9025     
9026     setElementSize : function(s, newSize, onComplete){
9027         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
9028     },
9029     
9030     moveSplitter : function(s){
9031         var yes = Roo.SplitBar;
9032         switch(s.placement){
9033             case yes.LEFT:
9034                 s.el.setX(s.resizingEl.getRight());
9035                 break;
9036             case yes.RIGHT:
9037                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
9038                 break;
9039             case yes.TOP:
9040                 s.el.setY(s.resizingEl.getBottom());
9041                 break;
9042             case yes.BOTTOM:
9043                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
9044                 break;
9045         }
9046     }
9047 };
9048
9049 /**
9050  * Orientation constant - Create a vertical SplitBar
9051  * @static
9052  * @type Number
9053  */
9054 Roo.SplitBar.VERTICAL = 1;
9055
9056 /**
9057  * Orientation constant - Create a horizontal SplitBar
9058  * @static
9059  * @type Number
9060  */
9061 Roo.SplitBar.HORIZONTAL = 2;
9062
9063 /**
9064  * Placement constant - The resizing element is to the left of the splitter element
9065  * @static
9066  * @type Number
9067  */
9068 Roo.SplitBar.LEFT = 1;
9069
9070 /**
9071  * Placement constant - The resizing element is to the right of the splitter element
9072  * @static
9073  * @type Number
9074  */
9075 Roo.SplitBar.RIGHT = 2;
9076
9077 /**
9078  * Placement constant - The resizing element is positioned above the splitter element
9079  * @static
9080  * @type Number
9081  */
9082 Roo.SplitBar.TOP = 3;
9083
9084 /**
9085  * Placement constant - The resizing element is positioned under splitter element
9086  * @static
9087  * @type Number
9088  */
9089 Roo.SplitBar.BOTTOM = 4;
9090 /*
9091  * Based on:
9092  * Ext JS Library 1.1.1
9093  * Copyright(c) 2006-2007, Ext JS, LLC.
9094  *
9095  * Originally Released Under LGPL - original licence link has changed is not relivant.
9096  *
9097  * Fork - LGPL
9098  * <script type="text/javascript">
9099  */
9100
9101 /**
9102  * @class Roo.View
9103  * @extends Roo.util.Observable
9104  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9105  * This class also supports single and multi selection modes. <br>
9106  * Create a data model bound view:
9107  <pre><code>
9108  var store = new Roo.data.Store(...);
9109
9110  var view = new Roo.View({
9111     el : "my-element",
9112     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9113  
9114     singleSelect: true,
9115     selectedClass: "ydataview-selected",
9116     store: store
9117  });
9118
9119  // listen for node click?
9120  view.on("click", function(vw, index, node, e){
9121  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9122  });
9123
9124  // load XML data
9125  dataModel.load("foobar.xml");
9126  </code></pre>
9127  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9128  * <br><br>
9129  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9130  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9131  * 
9132  * Note: old style constructor is still suported (container, template, config)
9133  * 
9134  * @constructor
9135  * Create a new View
9136  * @param {Object} config The config object
9137  * 
9138  */
9139 Roo.View = function(config, depreciated_tpl, depreciated_config){
9140     
9141     if (typeof(depreciated_tpl) == 'undefined') {
9142         // new way.. - universal constructor.
9143         Roo.apply(this, config);
9144         this.el  = Roo.get(this.el);
9145     } else {
9146         // old format..
9147         this.el  = Roo.get(config);
9148         this.tpl = depreciated_tpl;
9149         Roo.apply(this, depreciated_config);
9150     }
9151      
9152     
9153     if(typeof(this.tpl) == "string"){
9154         this.tpl = new Roo.Template(this.tpl);
9155     } else {
9156         // support xtype ctors..
9157         this.tpl = new Roo.factory(this.tpl, Roo);
9158     }
9159     
9160     
9161     this.tpl.compile();
9162    
9163
9164      
9165     /** @private */
9166     this.addEvents({
9167         /**
9168          * @event beforeclick
9169          * Fires before a click is processed. Returns false to cancel the default action.
9170          * @param {Roo.View} this
9171          * @param {Number} index The index of the target node
9172          * @param {HTMLElement} node The target node
9173          * @param {Roo.EventObject} e The raw event object
9174          */
9175             "beforeclick" : true,
9176         /**
9177          * @event click
9178          * Fires when a template node is clicked.
9179          * @param {Roo.View} this
9180          * @param {Number} index The index of the target node
9181          * @param {HTMLElement} node The target node
9182          * @param {Roo.EventObject} e The raw event object
9183          */
9184             "click" : true,
9185         /**
9186          * @event dblclick
9187          * Fires when a template node is double clicked.
9188          * @param {Roo.View} this
9189          * @param {Number} index The index of the target node
9190          * @param {HTMLElement} node The target node
9191          * @param {Roo.EventObject} e The raw event object
9192          */
9193             "dblclick" : true,
9194         /**
9195          * @event contextmenu
9196          * Fires when a template node is right clicked.
9197          * @param {Roo.View} this
9198          * @param {Number} index The index of the target node
9199          * @param {HTMLElement} node The target node
9200          * @param {Roo.EventObject} e The raw event object
9201          */
9202             "contextmenu" : true,
9203         /**
9204          * @event selectionchange
9205          * Fires when the selected nodes change.
9206          * @param {Roo.View} this
9207          * @param {Array} selections Array of the selected nodes
9208          */
9209             "selectionchange" : true,
9210     
9211         /**
9212          * @event beforeselect
9213          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9214          * @param {Roo.View} this
9215          * @param {HTMLElement} node The node to be selected
9216          * @param {Array} selections Array of currently selected nodes
9217          */
9218             "beforeselect" : true,
9219         /**
9220          * @event preparedata
9221          * Fires on every row to render, to allow you to change the data.
9222          * @param {Roo.View} this
9223          * @param {Object} data to be rendered (change this)
9224          */
9225           "preparedata" : true
9226         });
9227
9228     this.el.on({
9229         "click": this.onClick,
9230         "dblclick": this.onDblClick,
9231         "contextmenu": this.onContextMenu,
9232         scope:this
9233     });
9234
9235     this.selections = [];
9236     this.nodes = [];
9237     this.cmp = new Roo.CompositeElementLite([]);
9238     if(this.store){
9239         this.store = Roo.factory(this.store, Roo.data);
9240         this.setStore(this.store, true);
9241     }
9242     Roo.View.superclass.constructor.call(this);
9243 };
9244
9245 Roo.extend(Roo.View, Roo.util.Observable, {
9246     
9247      /**
9248      * @cfg {Roo.data.Store} store Data store to load data from.
9249      */
9250     store : false,
9251     
9252     /**
9253      * @cfg {String|Roo.Element} el The container element.
9254      */
9255     el : '',
9256     
9257     /**
9258      * @cfg {String|Roo.Template} tpl The template used by this View 
9259      */
9260     tpl : false,
9261     
9262     /**
9263      * @cfg {String} selectedClass The css class to add to selected nodes
9264      */
9265     selectedClass : "x-view-selected",
9266      /**
9267      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9268      */
9269     emptyText : "",
9270     /**
9271      * @cfg {Boolean} multiSelect Allow multiple selection
9272      */
9273     multiSelect : false,
9274     /**
9275      * @cfg {Boolean} singleSelect Allow single selection
9276      */
9277     singleSelect:  false,
9278     
9279     /**
9280      * @cfg {Boolean} toggleSelect - selecting 
9281      */
9282     toggleSelect : false,
9283     
9284     /**
9285      * Returns the element this view is bound to.
9286      * @return {Roo.Element}
9287      */
9288     getEl : function(){
9289         return this.el;
9290     },
9291
9292     /**
9293      * Refreshes the view.
9294      */
9295     refresh : function(){
9296         var t = this.tpl;
9297         this.clearSelections();
9298         this.el.update("");
9299         var html = [];
9300         var records = this.store.getRange();
9301         if(records.length < 1){
9302             this.el.update(this.emptyText);
9303             return;
9304         }
9305         for(var i = 0, len = records.length; i < len; i++){
9306             var data = this.prepareData(records[i].data, i, records[i]);
9307             this.fireEvent("preparedata", this, data, i, records[i]);
9308             html[html.length] = t.apply(data);
9309         }
9310         this.el.update(html.join(""));
9311         this.nodes = this.el.dom.childNodes;
9312         this.updateIndexes(0);
9313     },
9314
9315     /**
9316      * Function to override to reformat the data that is sent to
9317      * the template for each node.
9318      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9319      * a JSON object for an UpdateManager bound view).
9320      */
9321     prepareData : function(data){
9322         return data;
9323     },
9324
9325     onUpdate : function(ds, record){
9326         this.clearSelections();
9327         var index = this.store.indexOf(record);
9328         var n = this.nodes[index];
9329         this.tpl.insertBefore(n, this.prepareData(record.data));
9330         n.parentNode.removeChild(n);
9331         this.updateIndexes(index, index);
9332     },
9333
9334     onAdd : function(ds, records, index){
9335         this.clearSelections();
9336         if(this.nodes.length == 0){
9337             this.refresh();
9338             return;
9339         }
9340         var n = this.nodes[index];
9341         for(var i = 0, len = records.length; i < len; i++){
9342             var d = this.prepareData(records[i].data);
9343             if(n){
9344                 this.tpl.insertBefore(n, d);
9345             }else{
9346                 this.tpl.append(this.el, d);
9347             }
9348         }
9349         this.updateIndexes(index);
9350     },
9351
9352     onRemove : function(ds, record, index){
9353         this.clearSelections();
9354         this.el.dom.removeChild(this.nodes[index]);
9355         this.updateIndexes(index);
9356     },
9357
9358     /**
9359      * Refresh an individual node.
9360      * @param {Number} index
9361      */
9362     refreshNode : function(index){
9363         this.onUpdate(this.store, this.store.getAt(index));
9364     },
9365
9366     updateIndexes : function(startIndex, endIndex){
9367         var ns = this.nodes;
9368         startIndex = startIndex || 0;
9369         endIndex = endIndex || ns.length - 1;
9370         for(var i = startIndex; i <= endIndex; i++){
9371             ns[i].nodeIndex = i;
9372         }
9373     },
9374
9375     /**
9376      * Changes the data store this view uses and refresh the view.
9377      * @param {Store} store
9378      */
9379     setStore : function(store, initial){
9380         if(!initial && this.store){
9381             this.store.un("datachanged", this.refresh);
9382             this.store.un("add", this.onAdd);
9383             this.store.un("remove", this.onRemove);
9384             this.store.un("update", this.onUpdate);
9385             this.store.un("clear", this.refresh);
9386         }
9387         if(store){
9388           
9389             store.on("datachanged", this.refresh, this);
9390             store.on("add", this.onAdd, this);
9391             store.on("remove", this.onRemove, this);
9392             store.on("update", this.onUpdate, this);
9393             store.on("clear", this.refresh, this);
9394         }
9395         
9396         if(store){
9397             this.refresh();
9398         }
9399     },
9400
9401     /**
9402      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9403      * @param {HTMLElement} node
9404      * @return {HTMLElement} The template node
9405      */
9406     findItemFromChild : function(node){
9407         var el = this.el.dom;
9408         if(!node || node.parentNode == el){
9409                     return node;
9410             }
9411             var p = node.parentNode;
9412             while(p && p != el){
9413             if(p.parentNode == el){
9414                 return p;
9415             }
9416             p = p.parentNode;
9417         }
9418             return null;
9419     },
9420
9421     /** @ignore */
9422     onClick : function(e){
9423         var item = this.findItemFromChild(e.getTarget());
9424         if(item){
9425             var index = this.indexOf(item);
9426             if(this.onItemClick(item, index, e) !== false){
9427                 this.fireEvent("click", this, index, item, e);
9428             }
9429         }else{
9430             this.clearSelections();
9431         }
9432     },
9433
9434     /** @ignore */
9435     onContextMenu : function(e){
9436         var item = this.findItemFromChild(e.getTarget());
9437         if(item){
9438             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9439         }
9440     },
9441
9442     /** @ignore */
9443     onDblClick : function(e){
9444         var item = this.findItemFromChild(e.getTarget());
9445         if(item){
9446             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9447         }
9448     },
9449
9450     onItemClick : function(item, index, e)
9451     {
9452         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9453             return false;
9454         }
9455         if (this.toggleSelect) {
9456             var m = this.isSelected(item) ? 'unselect' : 'select';
9457             Roo.log(m);
9458             var _t = this;
9459             _t[m](item, true, false);
9460             return true;
9461         }
9462         if(this.multiSelect || this.singleSelect){
9463             if(this.multiSelect && e.shiftKey && this.lastSelection){
9464                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9465             }else{
9466                 this.select(item, this.multiSelect && e.ctrlKey);
9467                 this.lastSelection = item;
9468             }
9469             e.preventDefault();
9470         }
9471         return true;
9472     },
9473
9474     /**
9475      * Get the number of selected nodes.
9476      * @return {Number}
9477      */
9478     getSelectionCount : function(){
9479         return this.selections.length;
9480     },
9481
9482     /**
9483      * Get the currently selected nodes.
9484      * @return {Array} An array of HTMLElements
9485      */
9486     getSelectedNodes : function(){
9487         return this.selections;
9488     },
9489
9490     /**
9491      * Get the indexes of the selected nodes.
9492      * @return {Array}
9493      */
9494     getSelectedIndexes : function(){
9495         var indexes = [], s = this.selections;
9496         for(var i = 0, len = s.length; i < len; i++){
9497             indexes.push(s[i].nodeIndex);
9498         }
9499         return indexes;
9500     },
9501
9502     /**
9503      * Clear all selections
9504      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9505      */
9506     clearSelections : function(suppressEvent){
9507         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9508             this.cmp.elements = this.selections;
9509             this.cmp.removeClass(this.selectedClass);
9510             this.selections = [];
9511             if(!suppressEvent){
9512                 this.fireEvent("selectionchange", this, this.selections);
9513             }
9514         }
9515     },
9516
9517     /**
9518      * Returns true if the passed node is selected
9519      * @param {HTMLElement/Number} node The node or node index
9520      * @return {Boolean}
9521      */
9522     isSelected : function(node){
9523         var s = this.selections;
9524         if(s.length < 1){
9525             return false;
9526         }
9527         node = this.getNode(node);
9528         return s.indexOf(node) !== -1;
9529     },
9530
9531     /**
9532      * Selects nodes.
9533      * @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
9534      * @param {Boolean} keepExisting (optional) true to keep existing selections
9535      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9536      */
9537     select : function(nodeInfo, keepExisting, suppressEvent){
9538         if(nodeInfo instanceof Array){
9539             if(!keepExisting){
9540                 this.clearSelections(true);
9541             }
9542             for(var i = 0, len = nodeInfo.length; i < len; i++){
9543                 this.select(nodeInfo[i], true, true);
9544             }
9545             return;
9546         } 
9547         var node = this.getNode(nodeInfo);
9548         if(!node || this.isSelected(node)){
9549             return; // already selected.
9550         }
9551         if(!keepExisting){
9552             this.clearSelections(true);
9553         }
9554         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9555             Roo.fly(node).addClass(this.selectedClass);
9556             this.selections.push(node);
9557             if(!suppressEvent){
9558                 this.fireEvent("selectionchange", this, this.selections);
9559             }
9560         }
9561         
9562         
9563     },
9564       /**
9565      * Unselects nodes.
9566      * @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
9567      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9568      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9569      */
9570     unselect : function(nodeInfo, keepExisting, suppressEvent)
9571     {
9572         if(nodeInfo instanceof Array){
9573             Roo.each(this.selections, function(s) {
9574                 this.unselect(s, nodeInfo);
9575             }, this);
9576             return;
9577         }
9578         var node = this.getNode(nodeInfo);
9579         if(!node || !this.isSelected(node)){
9580             Roo.log("not selected");
9581             return; // not selected.
9582         }
9583         // fireevent???
9584         var ns = [];
9585         Roo.each(this.selections, function(s) {
9586             if (s == node ) {
9587                 Roo.fly(node).removeClass(this.selectedClass);
9588
9589                 return;
9590             }
9591             ns.push(s);
9592         },this);
9593         
9594         this.selections= ns;
9595         this.fireEvent("selectionchange", this, this.selections);
9596     },
9597
9598     /**
9599      * Gets a template node.
9600      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9601      * @return {HTMLElement} The node or null if it wasn't found
9602      */
9603     getNode : function(nodeInfo){
9604         if(typeof nodeInfo == "string"){
9605             return document.getElementById(nodeInfo);
9606         }else if(typeof nodeInfo == "number"){
9607             return this.nodes[nodeInfo];
9608         }
9609         return nodeInfo;
9610     },
9611
9612     /**
9613      * Gets a range template nodes.
9614      * @param {Number} startIndex
9615      * @param {Number} endIndex
9616      * @return {Array} An array of nodes
9617      */
9618     getNodes : function(start, end){
9619         var ns = this.nodes;
9620         start = start || 0;
9621         end = typeof end == "undefined" ? ns.length - 1 : end;
9622         var nodes = [];
9623         if(start <= end){
9624             for(var i = start; i <= end; i++){
9625                 nodes.push(ns[i]);
9626             }
9627         } else{
9628             for(var i = start; i >= end; i--){
9629                 nodes.push(ns[i]);
9630             }
9631         }
9632         return nodes;
9633     },
9634
9635     /**
9636      * Finds the index of the passed node
9637      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9638      * @return {Number} The index of the node or -1
9639      */
9640     indexOf : function(node){
9641         node = this.getNode(node);
9642         if(typeof node.nodeIndex == "number"){
9643             return node.nodeIndex;
9644         }
9645         var ns = this.nodes;
9646         for(var i = 0, len = ns.length; i < len; i++){
9647             if(ns[i] == node){
9648                 return i;
9649             }
9650         }
9651         return -1;
9652     }
9653 });
9654 /*
9655  * Based on:
9656  * Ext JS Library 1.1.1
9657  * Copyright(c) 2006-2007, Ext JS, LLC.
9658  *
9659  * Originally Released Under LGPL - original licence link has changed is not relivant.
9660  *
9661  * Fork - LGPL
9662  * <script type="text/javascript">
9663  */
9664
9665 /**
9666  * @class Roo.JsonView
9667  * @extends Roo.View
9668  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9669 <pre><code>
9670 var view = new Roo.JsonView({
9671     container: "my-element",
9672     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9673     multiSelect: true, 
9674     jsonRoot: "data" 
9675 });
9676
9677 // listen for node click?
9678 view.on("click", function(vw, index, node, e){
9679     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9680 });
9681
9682 // direct load of JSON data
9683 view.load("foobar.php");
9684
9685 // Example from my blog list
9686 var tpl = new Roo.Template(
9687     '&lt;div class="entry"&gt;' +
9688     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9689     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9690     "&lt;/div&gt;&lt;hr /&gt;"
9691 );
9692
9693 var moreView = new Roo.JsonView({
9694     container :  "entry-list", 
9695     template : tpl,
9696     jsonRoot: "posts"
9697 });
9698 moreView.on("beforerender", this.sortEntries, this);
9699 moreView.load({
9700     url: "/blog/get-posts.php",
9701     params: "allposts=true",
9702     text: "Loading Blog Entries..."
9703 });
9704 </code></pre>
9705
9706 * Note: old code is supported with arguments : (container, template, config)
9707
9708
9709  * @constructor
9710  * Create a new JsonView
9711  * 
9712  * @param {Object} config The config object
9713  * 
9714  */
9715 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9716     
9717     
9718     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9719
9720     var um = this.el.getUpdateManager();
9721     um.setRenderer(this);
9722     um.on("update", this.onLoad, this);
9723     um.on("failure", this.onLoadException, this);
9724
9725     /**
9726      * @event beforerender
9727      * Fires before rendering of the downloaded JSON data.
9728      * @param {Roo.JsonView} this
9729      * @param {Object} data The JSON data loaded
9730      */
9731     /**
9732      * @event load
9733      * Fires when data is loaded.
9734      * @param {Roo.JsonView} this
9735      * @param {Object} data The JSON data loaded
9736      * @param {Object} response The raw Connect response object
9737      */
9738     /**
9739      * @event loadexception
9740      * Fires when loading fails.
9741      * @param {Roo.JsonView} this
9742      * @param {Object} response The raw Connect response object
9743      */
9744     this.addEvents({
9745         'beforerender' : true,
9746         'load' : true,
9747         'loadexception' : true
9748     });
9749 };
9750 Roo.extend(Roo.JsonView, Roo.View, {
9751     /**
9752      * @type {String} The root property in the loaded JSON object that contains the data
9753      */
9754     jsonRoot : "",
9755
9756     /**
9757      * Refreshes the view.
9758      */
9759     refresh : function(){
9760         this.clearSelections();
9761         this.el.update("");
9762         var html = [];
9763         var o = this.jsonData;
9764         if(o && o.length > 0){
9765             for(var i = 0, len = o.length; i < len; i++){
9766                 var data = this.prepareData(o[i], i, o);
9767                 html[html.length] = this.tpl.apply(data);
9768             }
9769         }else{
9770             html.push(this.emptyText);
9771         }
9772         this.el.update(html.join(""));
9773         this.nodes = this.el.dom.childNodes;
9774         this.updateIndexes(0);
9775     },
9776
9777     /**
9778      * 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.
9779      * @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:
9780      <pre><code>
9781      view.load({
9782          url: "your-url.php",
9783          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9784          callback: yourFunction,
9785          scope: yourObject, //(optional scope)
9786          discardUrl: false,
9787          nocache: false,
9788          text: "Loading...",
9789          timeout: 30,
9790          scripts: false
9791      });
9792      </code></pre>
9793      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9794      * 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.
9795      * @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}
9796      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9797      * @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.
9798      */
9799     load : function(){
9800         var um = this.el.getUpdateManager();
9801         um.update.apply(um, arguments);
9802     },
9803
9804     render : function(el, response){
9805         this.clearSelections();
9806         this.el.update("");
9807         var o;
9808         try{
9809             o = Roo.util.JSON.decode(response.responseText);
9810             if(this.jsonRoot){
9811                 
9812                 o = o[this.jsonRoot];
9813             }
9814         } catch(e){
9815         }
9816         /**
9817          * The current JSON data or null
9818          */
9819         this.jsonData = o;
9820         this.beforeRender();
9821         this.refresh();
9822     },
9823
9824 /**
9825  * Get the number of records in the current JSON dataset
9826  * @return {Number}
9827  */
9828     getCount : function(){
9829         return this.jsonData ? this.jsonData.length : 0;
9830     },
9831
9832 /**
9833  * Returns the JSON object for the specified node(s)
9834  * @param {HTMLElement/Array} node The node or an array of nodes
9835  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9836  * you get the JSON object for the node
9837  */
9838     getNodeData : function(node){
9839         if(node instanceof Array){
9840             var data = [];
9841             for(var i = 0, len = node.length; i < len; i++){
9842                 data.push(this.getNodeData(node[i]));
9843             }
9844             return data;
9845         }
9846         return this.jsonData[this.indexOf(node)] || null;
9847     },
9848
9849     beforeRender : function(){
9850         this.snapshot = this.jsonData;
9851         if(this.sortInfo){
9852             this.sort.apply(this, this.sortInfo);
9853         }
9854         this.fireEvent("beforerender", this, this.jsonData);
9855     },
9856
9857     onLoad : function(el, o){
9858         this.fireEvent("load", this, this.jsonData, o);
9859     },
9860
9861     onLoadException : function(el, o){
9862         this.fireEvent("loadexception", this, o);
9863     },
9864
9865 /**
9866  * Filter the data by a specific property.
9867  * @param {String} property A property on your JSON objects
9868  * @param {String/RegExp} value Either string that the property values
9869  * should start with, or a RegExp to test against the property
9870  */
9871     filter : function(property, value){
9872         if(this.jsonData){
9873             var data = [];
9874             var ss = this.snapshot;
9875             if(typeof value == "string"){
9876                 var vlen = value.length;
9877                 if(vlen == 0){
9878                     this.clearFilter();
9879                     return;
9880                 }
9881                 value = value.toLowerCase();
9882                 for(var i = 0, len = ss.length; i < len; i++){
9883                     var o = ss[i];
9884                     if(o[property].substr(0, vlen).toLowerCase() == value){
9885                         data.push(o);
9886                     }
9887                 }
9888             } else if(value.exec){ // regex?
9889                 for(var i = 0, len = ss.length; i < len; i++){
9890                     var o = ss[i];
9891                     if(value.test(o[property])){
9892                         data.push(o);
9893                     }
9894                 }
9895             } else{
9896                 return;
9897             }
9898             this.jsonData = data;
9899             this.refresh();
9900         }
9901     },
9902
9903 /**
9904  * Filter by a function. The passed function will be called with each
9905  * object in the current dataset. If the function returns true the value is kept,
9906  * otherwise it is filtered.
9907  * @param {Function} fn
9908  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9909  */
9910     filterBy : function(fn, scope){
9911         if(this.jsonData){
9912             var data = [];
9913             var ss = this.snapshot;
9914             for(var i = 0, len = ss.length; i < len; i++){
9915                 var o = ss[i];
9916                 if(fn.call(scope || this, o)){
9917                     data.push(o);
9918                 }
9919             }
9920             this.jsonData = data;
9921             this.refresh();
9922         }
9923     },
9924
9925 /**
9926  * Clears the current filter.
9927  */
9928     clearFilter : function(){
9929         if(this.snapshot && this.jsonData != this.snapshot){
9930             this.jsonData = this.snapshot;
9931             this.refresh();
9932         }
9933     },
9934
9935
9936 /**
9937  * Sorts the data for this view and refreshes it.
9938  * @param {String} property A property on your JSON objects to sort on
9939  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9940  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9941  */
9942     sort : function(property, dir, sortType){
9943         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9944         if(this.jsonData){
9945             var p = property;
9946             var dsc = dir && dir.toLowerCase() == "desc";
9947             var f = function(o1, o2){
9948                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9949                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9950                 ;
9951                 if(v1 < v2){
9952                     return dsc ? +1 : -1;
9953                 } else if(v1 > v2){
9954                     return dsc ? -1 : +1;
9955                 } else{
9956                     return 0;
9957                 }
9958             };
9959             this.jsonData.sort(f);
9960             this.refresh();
9961             if(this.jsonData != this.snapshot){
9962                 this.snapshot.sort(f);
9963             }
9964         }
9965     }
9966 });/*
9967  * Based on:
9968  * Ext JS Library 1.1.1
9969  * Copyright(c) 2006-2007, Ext JS, LLC.
9970  *
9971  * Originally Released Under LGPL - original licence link has changed is not relivant.
9972  *
9973  * Fork - LGPL
9974  * <script type="text/javascript">
9975  */
9976  
9977
9978 /**
9979  * @class Roo.ColorPalette
9980  * @extends Roo.Component
9981  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
9982  * Here's an example of typical usage:
9983  * <pre><code>
9984 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
9985 cp.render('my-div');
9986
9987 cp.on('select', function(palette, selColor){
9988     // do something with selColor
9989 });
9990 </code></pre>
9991  * @constructor
9992  * Create a new ColorPalette
9993  * @param {Object} config The config object
9994  */
9995 Roo.ColorPalette = function(config){
9996     Roo.ColorPalette.superclass.constructor.call(this, config);
9997     this.addEvents({
9998         /**
9999              * @event select
10000              * Fires when a color is selected
10001              * @param {ColorPalette} this
10002              * @param {String} color The 6-digit color hex code (without the # symbol)
10003              */
10004         select: true
10005     });
10006
10007     if(this.handler){
10008         this.on("select", this.handler, this.scope, true);
10009     }
10010 };
10011 Roo.extend(Roo.ColorPalette, Roo.Component, {
10012     /**
10013      * @cfg {String} itemCls
10014      * The CSS class to apply to the containing element (defaults to "x-color-palette")
10015      */
10016     itemCls : "x-color-palette",
10017     /**
10018      * @cfg {String} value
10019      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
10020      * the hex codes are case-sensitive.
10021      */
10022     value : null,
10023     clickEvent:'click',
10024     // private
10025     ctype: "Roo.ColorPalette",
10026
10027     /**
10028      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
10029      */
10030     allowReselect : false,
10031
10032     /**
10033      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
10034      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
10035      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
10036      * of colors with the width setting until the box is symmetrical.</p>
10037      * <p>You can override individual colors if needed:</p>
10038      * <pre><code>
10039 var cp = new Roo.ColorPalette();
10040 cp.colors[0] = "FF0000";  // change the first box to red
10041 </code></pre>
10042
10043 Or you can provide a custom array of your own for complete control:
10044 <pre><code>
10045 var cp = new Roo.ColorPalette();
10046 cp.colors = ["000000", "993300", "333300"];
10047 </code></pre>
10048      * @type Array
10049      */
10050     colors : [
10051         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
10052         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
10053         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
10054         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
10055         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
10056     ],
10057
10058     // private
10059     onRender : function(container, position){
10060         var t = new Roo.MasterTemplate(
10061             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
10062         );
10063         var c = this.colors;
10064         for(var i = 0, len = c.length; i < len; i++){
10065             t.add([c[i]]);
10066         }
10067         var el = document.createElement("div");
10068         el.className = this.itemCls;
10069         t.overwrite(el);
10070         container.dom.insertBefore(el, position);
10071         this.el = Roo.get(el);
10072         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
10073         if(this.clickEvent != 'click'){
10074             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
10075         }
10076     },
10077
10078     // private
10079     afterRender : function(){
10080         Roo.ColorPalette.superclass.afterRender.call(this);
10081         if(this.value){
10082             var s = this.value;
10083             this.value = null;
10084             this.select(s);
10085         }
10086     },
10087
10088     // private
10089     handleClick : function(e, t){
10090         e.preventDefault();
10091         if(!this.disabled){
10092             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
10093             this.select(c.toUpperCase());
10094         }
10095     },
10096
10097     /**
10098      * Selects the specified color in the palette (fires the select event)
10099      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
10100      */
10101     select : function(color){
10102         color = color.replace("#", "");
10103         if(color != this.value || this.allowReselect){
10104             var el = this.el;
10105             if(this.value){
10106                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
10107             }
10108             el.child("a.color-"+color).addClass("x-color-palette-sel");
10109             this.value = color;
10110             this.fireEvent("select", this, color);
10111         }
10112     }
10113 });/*
10114  * Based on:
10115  * Ext JS Library 1.1.1
10116  * Copyright(c) 2006-2007, Ext JS, LLC.
10117  *
10118  * Originally Released Under LGPL - original licence link has changed is not relivant.
10119  *
10120  * Fork - LGPL
10121  * <script type="text/javascript">
10122  */
10123  
10124 /**
10125  * @class Roo.DatePicker
10126  * @extends Roo.Component
10127  * Simple date picker class.
10128  * @constructor
10129  * Create a new DatePicker
10130  * @param {Object} config The config object
10131  */
10132 Roo.DatePicker = function(config){
10133     Roo.DatePicker.superclass.constructor.call(this, config);
10134
10135     this.value = config && config.value ?
10136                  config.value.clearTime() : new Date().clearTime();
10137
10138     this.addEvents({
10139         /**
10140              * @event select
10141              * Fires when a date is selected
10142              * @param {DatePicker} this
10143              * @param {Date} date The selected date
10144              */
10145         'select': true,
10146         /**
10147              * @event monthchange
10148              * Fires when the displayed month changes 
10149              * @param {DatePicker} this
10150              * @param {Date} date The selected month
10151              */
10152         'monthchange': true
10153     });
10154
10155     if(this.handler){
10156         this.on("select", this.handler,  this.scope || this);
10157     }
10158     // build the disabledDatesRE
10159     if(!this.disabledDatesRE && this.disabledDates){
10160         var dd = this.disabledDates;
10161         var re = "(?:";
10162         for(var i = 0; i < dd.length; i++){
10163             re += dd[i];
10164             if(i != dd.length-1) re += "|";
10165         }
10166         this.disabledDatesRE = new RegExp(re + ")");
10167     }
10168 };
10169
10170 Roo.extend(Roo.DatePicker, Roo.Component, {
10171     /**
10172      * @cfg {String} todayText
10173      * The text to display on the button that selects the current date (defaults to "Today")
10174      */
10175     todayText : "Today",
10176     /**
10177      * @cfg {String} okText
10178      * The text to display on the ok button
10179      */
10180     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10181     /**
10182      * @cfg {String} cancelText
10183      * The text to display on the cancel button
10184      */
10185     cancelText : "Cancel",
10186     /**
10187      * @cfg {String} todayTip
10188      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10189      */
10190     todayTip : "{0} (Spacebar)",
10191     /**
10192      * @cfg {Date} minDate
10193      * Minimum allowable date (JavaScript date object, defaults to null)
10194      */
10195     minDate : null,
10196     /**
10197      * @cfg {Date} maxDate
10198      * Maximum allowable date (JavaScript date object, defaults to null)
10199      */
10200     maxDate : null,
10201     /**
10202      * @cfg {String} minText
10203      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10204      */
10205     minText : "This date is before the minimum date",
10206     /**
10207      * @cfg {String} maxText
10208      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10209      */
10210     maxText : "This date is after the maximum date",
10211     /**
10212      * @cfg {String} format
10213      * The default date format string which can be overriden for localization support.  The format must be
10214      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10215      */
10216     format : "m/d/y",
10217     /**
10218      * @cfg {Array} disabledDays
10219      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10220      */
10221     disabledDays : null,
10222     /**
10223      * @cfg {String} disabledDaysText
10224      * The tooltip to display when the date falls on a disabled day (defaults to "")
10225      */
10226     disabledDaysText : "",
10227     /**
10228      * @cfg {RegExp} disabledDatesRE
10229      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10230      */
10231     disabledDatesRE : null,
10232     /**
10233      * @cfg {String} disabledDatesText
10234      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10235      */
10236     disabledDatesText : "",
10237     /**
10238      * @cfg {Boolean} constrainToViewport
10239      * True to constrain the date picker to the viewport (defaults to true)
10240      */
10241     constrainToViewport : true,
10242     /**
10243      * @cfg {Array} monthNames
10244      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10245      */
10246     monthNames : Date.monthNames,
10247     /**
10248      * @cfg {Array} dayNames
10249      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10250      */
10251     dayNames : Date.dayNames,
10252     /**
10253      * @cfg {String} nextText
10254      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10255      */
10256     nextText: 'Next Month (Control+Right)',
10257     /**
10258      * @cfg {String} prevText
10259      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10260      */
10261     prevText: 'Previous Month (Control+Left)',
10262     /**
10263      * @cfg {String} monthYearText
10264      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10265      */
10266     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10267     /**
10268      * @cfg {Number} startDay
10269      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10270      */
10271     startDay : 0,
10272     /**
10273      * @cfg {Bool} showClear
10274      * Show a clear button (usefull for date form elements that can be blank.)
10275      */
10276     
10277     showClear: false,
10278     
10279     /**
10280      * Sets the value of the date field
10281      * @param {Date} value The date to set
10282      */
10283     setValue : function(value){
10284         var old = this.value;
10285         this.value = value.clearTime(true);
10286         if(this.el){
10287             this.update(this.value);
10288         }
10289     },
10290
10291     /**
10292      * Gets the current selected value of the date field
10293      * @return {Date} The selected date
10294      */
10295     getValue : function(){
10296         return this.value;
10297     },
10298
10299     // private
10300     focus : function(){
10301         if(this.el){
10302             this.update(this.activeDate);
10303         }
10304     },
10305
10306     // private
10307     onRender : function(container, position){
10308         var m = [
10309              '<table cellspacing="0">',
10310                 '<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>',
10311                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10312         var dn = this.dayNames;
10313         for(var i = 0; i < 7; i++){
10314             var d = this.startDay+i;
10315             if(d > 6){
10316                 d = d-7;
10317             }
10318             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10319         }
10320         m[m.length] = "</tr></thead><tbody><tr>";
10321         for(var i = 0; i < 42; i++) {
10322             if(i % 7 == 0 && i != 0){
10323                 m[m.length] = "</tr><tr>";
10324             }
10325             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10326         }
10327         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10328             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10329
10330         var el = document.createElement("div");
10331         el.className = "x-date-picker";
10332         el.innerHTML = m.join("");
10333
10334         container.dom.insertBefore(el, position);
10335
10336         this.el = Roo.get(el);
10337         this.eventEl = Roo.get(el.firstChild);
10338
10339         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10340             handler: this.showPrevMonth,
10341             scope: this,
10342             preventDefault:true,
10343             stopDefault:true
10344         });
10345
10346         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10347             handler: this.showNextMonth,
10348             scope: this,
10349             preventDefault:true,
10350             stopDefault:true
10351         });
10352
10353         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10354
10355         this.monthPicker = this.el.down('div.x-date-mp');
10356         this.monthPicker.enableDisplayMode('block');
10357         
10358         var kn = new Roo.KeyNav(this.eventEl, {
10359             "left" : function(e){
10360                 e.ctrlKey ?
10361                     this.showPrevMonth() :
10362                     this.update(this.activeDate.add("d", -1));
10363             },
10364
10365             "right" : function(e){
10366                 e.ctrlKey ?
10367                     this.showNextMonth() :
10368                     this.update(this.activeDate.add("d", 1));
10369             },
10370
10371             "up" : function(e){
10372                 e.ctrlKey ?
10373                     this.showNextYear() :
10374                     this.update(this.activeDate.add("d", -7));
10375             },
10376
10377             "down" : function(e){
10378                 e.ctrlKey ?
10379                     this.showPrevYear() :
10380                     this.update(this.activeDate.add("d", 7));
10381             },
10382
10383             "pageUp" : function(e){
10384                 this.showNextMonth();
10385             },
10386
10387             "pageDown" : function(e){
10388                 this.showPrevMonth();
10389             },
10390
10391             "enter" : function(e){
10392                 e.stopPropagation();
10393                 return true;
10394             },
10395
10396             scope : this
10397         });
10398
10399         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10400
10401         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10402
10403         this.el.unselectable();
10404         
10405         this.cells = this.el.select("table.x-date-inner tbody td");
10406         this.textNodes = this.el.query("table.x-date-inner tbody span");
10407
10408         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10409             text: "&#160;",
10410             tooltip: this.monthYearText
10411         });
10412
10413         this.mbtn.on('click', this.showMonthPicker, this);
10414         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10415
10416
10417         var today = (new Date()).dateFormat(this.format);
10418         
10419         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10420         if (this.showClear) {
10421             baseTb.add( new Roo.Toolbar.Fill());
10422         }
10423         baseTb.add({
10424             text: String.format(this.todayText, today),
10425             tooltip: String.format(this.todayTip, today),
10426             handler: this.selectToday,
10427             scope: this
10428         });
10429         
10430         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10431             
10432         //});
10433         if (this.showClear) {
10434             
10435             baseTb.add( new Roo.Toolbar.Fill());
10436             baseTb.add({
10437                 text: '&#160;',
10438                 cls: 'x-btn-icon x-btn-clear',
10439                 handler: function() {
10440                     //this.value = '';
10441                     this.fireEvent("select", this, '');
10442                 },
10443                 scope: this
10444             });
10445         }
10446         
10447         
10448         if(Roo.isIE){
10449             this.el.repaint();
10450         }
10451         this.update(this.value);
10452     },
10453
10454     createMonthPicker : function(){
10455         if(!this.monthPicker.dom.firstChild){
10456             var buf = ['<table border="0" cellspacing="0">'];
10457             for(var i = 0; i < 6; i++){
10458                 buf.push(
10459                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10460                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10461                     i == 0 ?
10462                     '<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>' :
10463                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10464                 );
10465             }
10466             buf.push(
10467                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10468                     this.okText,
10469                     '</button><button type="button" class="x-date-mp-cancel">',
10470                     this.cancelText,
10471                     '</button></td></tr>',
10472                 '</table>'
10473             );
10474             this.monthPicker.update(buf.join(''));
10475             this.monthPicker.on('click', this.onMonthClick, this);
10476             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10477
10478             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10479             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10480
10481             this.mpMonths.each(function(m, a, i){
10482                 i += 1;
10483                 if((i%2) == 0){
10484                     m.dom.xmonth = 5 + Math.round(i * .5);
10485                 }else{
10486                     m.dom.xmonth = Math.round((i-1) * .5);
10487                 }
10488             });
10489         }
10490     },
10491
10492     showMonthPicker : function(){
10493         this.createMonthPicker();
10494         var size = this.el.getSize();
10495         this.monthPicker.setSize(size);
10496         this.monthPicker.child('table').setSize(size);
10497
10498         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10499         this.updateMPMonth(this.mpSelMonth);
10500         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10501         this.updateMPYear(this.mpSelYear);
10502
10503         this.monthPicker.slideIn('t', {duration:.2});
10504     },
10505
10506     updateMPYear : function(y){
10507         this.mpyear = y;
10508         var ys = this.mpYears.elements;
10509         for(var i = 1; i <= 10; i++){
10510             var td = ys[i-1], y2;
10511             if((i%2) == 0){
10512                 y2 = y + Math.round(i * .5);
10513                 td.firstChild.innerHTML = y2;
10514                 td.xyear = y2;
10515             }else{
10516                 y2 = y - (5-Math.round(i * .5));
10517                 td.firstChild.innerHTML = y2;
10518                 td.xyear = y2;
10519             }
10520             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10521         }
10522     },
10523
10524     updateMPMonth : function(sm){
10525         this.mpMonths.each(function(m, a, i){
10526             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10527         });
10528     },
10529
10530     selectMPMonth: function(m){
10531         
10532     },
10533
10534     onMonthClick : function(e, t){
10535         e.stopEvent();
10536         var el = new Roo.Element(t), pn;
10537         if(el.is('button.x-date-mp-cancel')){
10538             this.hideMonthPicker();
10539         }
10540         else if(el.is('button.x-date-mp-ok')){
10541             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10542             this.hideMonthPicker();
10543         }
10544         else if(pn = el.up('td.x-date-mp-month', 2)){
10545             this.mpMonths.removeClass('x-date-mp-sel');
10546             pn.addClass('x-date-mp-sel');
10547             this.mpSelMonth = pn.dom.xmonth;
10548         }
10549         else if(pn = el.up('td.x-date-mp-year', 2)){
10550             this.mpYears.removeClass('x-date-mp-sel');
10551             pn.addClass('x-date-mp-sel');
10552             this.mpSelYear = pn.dom.xyear;
10553         }
10554         else if(el.is('a.x-date-mp-prev')){
10555             this.updateMPYear(this.mpyear-10);
10556         }
10557         else if(el.is('a.x-date-mp-next')){
10558             this.updateMPYear(this.mpyear+10);
10559         }
10560     },
10561
10562     onMonthDblClick : function(e, t){
10563         e.stopEvent();
10564         var el = new Roo.Element(t), pn;
10565         if(pn = el.up('td.x-date-mp-month', 2)){
10566             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10567             this.hideMonthPicker();
10568         }
10569         else if(pn = el.up('td.x-date-mp-year', 2)){
10570             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10571             this.hideMonthPicker();
10572         }
10573     },
10574
10575     hideMonthPicker : function(disableAnim){
10576         if(this.monthPicker){
10577             if(disableAnim === true){
10578                 this.monthPicker.hide();
10579             }else{
10580                 this.monthPicker.slideOut('t', {duration:.2});
10581             }
10582         }
10583     },
10584
10585     // private
10586     showPrevMonth : function(e){
10587         this.update(this.activeDate.add("mo", -1));
10588     },
10589
10590     // private
10591     showNextMonth : function(e){
10592         this.update(this.activeDate.add("mo", 1));
10593     },
10594
10595     // private
10596     showPrevYear : function(){
10597         this.update(this.activeDate.add("y", -1));
10598     },
10599
10600     // private
10601     showNextYear : function(){
10602         this.update(this.activeDate.add("y", 1));
10603     },
10604
10605     // private
10606     handleMouseWheel : function(e){
10607         var delta = e.getWheelDelta();
10608         if(delta > 0){
10609             this.showPrevMonth();
10610             e.stopEvent();
10611         } else if(delta < 0){
10612             this.showNextMonth();
10613             e.stopEvent();
10614         }
10615     },
10616
10617     // private
10618     handleDateClick : function(e, t){
10619         e.stopEvent();
10620         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10621             this.setValue(new Date(t.dateValue));
10622             this.fireEvent("select", this, this.value);
10623         }
10624     },
10625
10626     // private
10627     selectToday : function(){
10628         this.setValue(new Date().clearTime());
10629         this.fireEvent("select", this, this.value);
10630     },
10631
10632     // private
10633     update : function(date)
10634     {
10635         var vd = this.activeDate;
10636         this.activeDate = date;
10637         if(vd && this.el){
10638             var t = date.getTime();
10639             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10640                 this.cells.removeClass("x-date-selected");
10641                 this.cells.each(function(c){
10642                    if(c.dom.firstChild.dateValue == t){
10643                        c.addClass("x-date-selected");
10644                        setTimeout(function(){
10645                             try{c.dom.firstChild.focus();}catch(e){}
10646                        }, 50);
10647                        return false;
10648                    }
10649                 });
10650                 return;
10651             }
10652         }
10653         
10654         var days = date.getDaysInMonth();
10655         var firstOfMonth = date.getFirstDateOfMonth();
10656         var startingPos = firstOfMonth.getDay()-this.startDay;
10657
10658         if(startingPos <= this.startDay){
10659             startingPos += 7;
10660         }
10661
10662         var pm = date.add("mo", -1);
10663         var prevStart = pm.getDaysInMonth()-startingPos;
10664
10665         var cells = this.cells.elements;
10666         var textEls = this.textNodes;
10667         days += startingPos;
10668
10669         // convert everything to numbers so it's fast
10670         var day = 86400000;
10671         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10672         var today = new Date().clearTime().getTime();
10673         var sel = date.clearTime().getTime();
10674         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10675         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10676         var ddMatch = this.disabledDatesRE;
10677         var ddText = this.disabledDatesText;
10678         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10679         var ddaysText = this.disabledDaysText;
10680         var format = this.format;
10681
10682         var setCellClass = function(cal, cell){
10683             cell.title = "";
10684             var t = d.getTime();
10685             cell.firstChild.dateValue = t;
10686             if(t == today){
10687                 cell.className += " x-date-today";
10688                 cell.title = cal.todayText;
10689             }
10690             if(t == sel){
10691                 cell.className += " x-date-selected";
10692                 setTimeout(function(){
10693                     try{cell.firstChild.focus();}catch(e){}
10694                 }, 50);
10695             }
10696             // disabling
10697             if(t < min) {
10698                 cell.className = " x-date-disabled";
10699                 cell.title = cal.minText;
10700                 return;
10701             }
10702             if(t > max) {
10703                 cell.className = " x-date-disabled";
10704                 cell.title = cal.maxText;
10705                 return;
10706             }
10707             if(ddays){
10708                 if(ddays.indexOf(d.getDay()) != -1){
10709                     cell.title = ddaysText;
10710                     cell.className = " x-date-disabled";
10711                 }
10712             }
10713             if(ddMatch && format){
10714                 var fvalue = d.dateFormat(format);
10715                 if(ddMatch.test(fvalue)){
10716                     cell.title = ddText.replace("%0", fvalue);
10717                     cell.className = " x-date-disabled";
10718                 }
10719             }
10720         };
10721
10722         var i = 0;
10723         for(; i < startingPos; i++) {
10724             textEls[i].innerHTML = (++prevStart);
10725             d.setDate(d.getDate()+1);
10726             cells[i].className = "x-date-prevday";
10727             setCellClass(this, cells[i]);
10728         }
10729         for(; i < days; i++){
10730             intDay = i - startingPos + 1;
10731             textEls[i].innerHTML = (intDay);
10732             d.setDate(d.getDate()+1);
10733             cells[i].className = "x-date-active";
10734             setCellClass(this, cells[i]);
10735         }
10736         var extraDays = 0;
10737         for(; i < 42; i++) {
10738              textEls[i].innerHTML = (++extraDays);
10739              d.setDate(d.getDate()+1);
10740              cells[i].className = "x-date-nextday";
10741              setCellClass(this, cells[i]);
10742         }
10743
10744         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10745         this.fireEvent('monthchange', this, date);
10746         
10747         if(!this.internalRender){
10748             var main = this.el.dom.firstChild;
10749             var w = main.offsetWidth;
10750             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10751             Roo.fly(main).setWidth(w);
10752             this.internalRender = true;
10753             // opera does not respect the auto grow header center column
10754             // then, after it gets a width opera refuses to recalculate
10755             // without a second pass
10756             if(Roo.isOpera && !this.secondPass){
10757                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10758                 this.secondPass = true;
10759                 this.update.defer(10, this, [date]);
10760             }
10761         }
10762         
10763         
10764     }
10765 });        /*
10766  * Based on:
10767  * Ext JS Library 1.1.1
10768  * Copyright(c) 2006-2007, Ext JS, LLC.
10769  *
10770  * Originally Released Under LGPL - original licence link has changed is not relivant.
10771  *
10772  * Fork - LGPL
10773  * <script type="text/javascript">
10774  */
10775 /**
10776  * @class Roo.TabPanel
10777  * @extends Roo.util.Observable
10778  * A lightweight tab container.
10779  * <br><br>
10780  * Usage:
10781  * <pre><code>
10782 // basic tabs 1, built from existing content
10783 var tabs = new Roo.TabPanel("tabs1");
10784 tabs.addTab("script", "View Script");
10785 tabs.addTab("markup", "View Markup");
10786 tabs.activate("script");
10787
10788 // more advanced tabs, built from javascript
10789 var jtabs = new Roo.TabPanel("jtabs");
10790 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10791
10792 // set up the UpdateManager
10793 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10794 var updater = tab2.getUpdateManager();
10795 updater.setDefaultUrl("ajax1.htm");
10796 tab2.on('activate', updater.refresh, updater, true);
10797
10798 // Use setUrl for Ajax loading
10799 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10800 tab3.setUrl("ajax2.htm", null, true);
10801
10802 // Disabled tab
10803 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10804 tab4.disable();
10805
10806 jtabs.activate("jtabs-1");
10807  * </code></pre>
10808  * @constructor
10809  * Create a new TabPanel.
10810  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10811  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10812  */
10813 Roo.TabPanel = function(container, config){
10814     /**
10815     * The container element for this TabPanel.
10816     * @type Roo.Element
10817     */
10818     this.el = Roo.get(container, true);
10819     if(config){
10820         if(typeof config == "boolean"){
10821             this.tabPosition = config ? "bottom" : "top";
10822         }else{
10823             Roo.apply(this, config);
10824         }
10825     }
10826     if(this.tabPosition == "bottom"){
10827         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10828         this.el.addClass("x-tabs-bottom");
10829     }
10830     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10831     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10832     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10833     if(Roo.isIE){
10834         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10835     }
10836     if(this.tabPosition != "bottom"){
10837         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
10838          * @type Roo.Element
10839          */
10840         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10841         this.el.addClass("x-tabs-top");
10842     }
10843     this.items = [];
10844
10845     this.bodyEl.setStyle("position", "relative");
10846
10847     this.active = null;
10848     this.activateDelegate = this.activate.createDelegate(this);
10849
10850     this.addEvents({
10851         /**
10852          * @event tabchange
10853          * Fires when the active tab changes
10854          * @param {Roo.TabPanel} this
10855          * @param {Roo.TabPanelItem} activePanel The new active tab
10856          */
10857         "tabchange": true,
10858         /**
10859          * @event beforetabchange
10860          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10861          * @param {Roo.TabPanel} this
10862          * @param {Object} e Set cancel to true on this object to cancel the tab change
10863          * @param {Roo.TabPanelItem} tab The tab being changed to
10864          */
10865         "beforetabchange" : true
10866     });
10867
10868     Roo.EventManager.onWindowResize(this.onResize, this);
10869     this.cpad = this.el.getPadding("lr");
10870     this.hiddenCount = 0;
10871
10872
10873     // toolbar on the tabbar support...
10874     if (this.toolbar) {
10875         var tcfg = this.toolbar;
10876         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
10877         this.toolbar = new Roo.Toolbar(tcfg);
10878         if (Roo.isSafari) {
10879             var tbl = tcfg.container.child('table', true);
10880             tbl.setAttribute('width', '100%');
10881         }
10882         
10883     }
10884    
10885
10886
10887     Roo.TabPanel.superclass.constructor.call(this);
10888 };
10889
10890 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10891     /*
10892      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10893      */
10894     tabPosition : "top",
10895     /*
10896      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10897      */
10898     currentTabWidth : 0,
10899     /*
10900      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10901      */
10902     minTabWidth : 40,
10903     /*
10904      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10905      */
10906     maxTabWidth : 250,
10907     /*
10908      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10909      */
10910     preferredTabWidth : 175,
10911     /*
10912      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10913      */
10914     resizeTabs : false,
10915     /*
10916      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10917      */
10918     monitorResize : true,
10919     /*
10920      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
10921      */
10922     toolbar : false,
10923
10924     /**
10925      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10926      * @param {String} id The id of the div to use <b>or create</b>
10927      * @param {String} text The text for the tab
10928      * @param {String} content (optional) Content to put in the TabPanelItem body
10929      * @param {Boolean} closable (optional) True to create a close icon on the tab
10930      * @return {Roo.TabPanelItem} The created TabPanelItem
10931      */
10932     addTab : function(id, text, content, closable){
10933         var item = new Roo.TabPanelItem(this, id, text, closable);
10934         this.addTabItem(item);
10935         if(content){
10936             item.setContent(content);
10937         }
10938         return item;
10939     },
10940
10941     /**
10942      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10943      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10944      * @return {Roo.TabPanelItem}
10945      */
10946     getTab : function(id){
10947         return this.items[id];
10948     },
10949
10950     /**
10951      * Hides the {@link Roo.TabPanelItem} with the specified id/index
10952      * @param {String/Number} id The id or index of the TabPanelItem to hide.
10953      */
10954     hideTab : function(id){
10955         var t = this.items[id];
10956         if(!t.isHidden()){
10957            t.setHidden(true);
10958            this.hiddenCount++;
10959            this.autoSizeTabs();
10960         }
10961     },
10962
10963     /**
10964      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10965      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10966      */
10967     unhideTab : function(id){
10968         var t = this.items[id];
10969         if(t.isHidden()){
10970            t.setHidden(false);
10971            this.hiddenCount--;
10972            this.autoSizeTabs();
10973         }
10974     },
10975
10976     /**
10977      * Adds an existing {@link Roo.TabPanelItem}.
10978      * @param {Roo.TabPanelItem} item The TabPanelItem to add
10979      */
10980     addTabItem : function(item){
10981         this.items[item.id] = item;
10982         this.items.push(item);
10983         if(this.resizeTabs){
10984            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10985            this.autoSizeTabs();
10986         }else{
10987             item.autoSize();
10988         }
10989     },
10990
10991     /**
10992      * Removes a {@link Roo.TabPanelItem}.
10993      * @param {String/Number} id The id or index of the TabPanelItem to remove.
10994      */
10995     removeTab : function(id){
10996         var items = this.items;
10997         var tab = items[id];
10998         if(!tab) { return; }
10999         var index = items.indexOf(tab);
11000         if(this.active == tab && items.length > 1){
11001             var newTab = this.getNextAvailable(index);
11002             if(newTab) {
11003                 newTab.activate();
11004             }
11005         }
11006         this.stripEl.dom.removeChild(tab.pnode.dom);
11007         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
11008             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
11009         }
11010         items.splice(index, 1);
11011         delete this.items[tab.id];
11012         tab.fireEvent("close", tab);
11013         tab.purgeListeners();
11014         this.autoSizeTabs();
11015     },
11016
11017     getNextAvailable : function(start){
11018         var items = this.items;
11019         var index = start;
11020         // look for a next tab that will slide over to
11021         // replace the one being removed
11022         while(index < items.length){
11023             var item = items[++index];
11024             if(item && !item.isHidden()){
11025                 return item;
11026             }
11027         }
11028         // if one isn't found select the previous tab (on the left)
11029         index = start;
11030         while(index >= 0){
11031             var item = items[--index];
11032             if(item && !item.isHidden()){
11033                 return item;
11034             }
11035         }
11036         return null;
11037     },
11038
11039     /**
11040      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
11041      * @param {String/Number} id The id or index of the TabPanelItem to disable.
11042      */
11043     disableTab : function(id){
11044         var tab = this.items[id];
11045         if(tab && this.active != tab){
11046             tab.disable();
11047         }
11048     },
11049
11050     /**
11051      * Enables a {@link Roo.TabPanelItem} that is disabled.
11052      * @param {String/Number} id The id or index of the TabPanelItem to enable.
11053      */
11054     enableTab : function(id){
11055         var tab = this.items[id];
11056         tab.enable();
11057     },
11058
11059     /**
11060      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
11061      * @param {String/Number} id The id or index of the TabPanelItem to activate.
11062      * @return {Roo.TabPanelItem} The TabPanelItem.
11063      */
11064     activate : function(id){
11065         var tab = this.items[id];
11066         if(!tab){
11067             return null;
11068         }
11069         if(tab == this.active || tab.disabled){
11070             return tab;
11071         }
11072         var e = {};
11073         this.fireEvent("beforetabchange", this, e, tab);
11074         if(e.cancel !== true && !tab.disabled){
11075             if(this.active){
11076                 this.active.hide();
11077             }
11078             this.active = this.items[id];
11079             this.active.show();
11080             this.fireEvent("tabchange", this, this.active);
11081         }
11082         return tab;
11083     },
11084
11085     /**
11086      * Gets the active {@link Roo.TabPanelItem}.
11087      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
11088      */
11089     getActiveTab : function(){
11090         return this.active;
11091     },
11092
11093     /**
11094      * Updates the tab body element to fit the height of the container element
11095      * for overflow scrolling
11096      * @param {Number} targetHeight (optional) Override the starting height from the elements height
11097      */
11098     syncHeight : function(targetHeight){
11099         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
11100         var bm = this.bodyEl.getMargins();
11101         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
11102         this.bodyEl.setHeight(newHeight);
11103         return newHeight;
11104     },
11105
11106     onResize : function(){
11107         if(this.monitorResize){
11108             this.autoSizeTabs();
11109         }
11110     },
11111
11112     /**
11113      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
11114      */
11115     beginUpdate : function(){
11116         this.updating = true;
11117     },
11118
11119     /**
11120      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
11121      */
11122     endUpdate : function(){
11123         this.updating = false;
11124         this.autoSizeTabs();
11125     },
11126
11127     /**
11128      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
11129      */
11130     autoSizeTabs : function(){
11131         var count = this.items.length;
11132         var vcount = count - this.hiddenCount;
11133         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
11134         var w = Math.max(this.el.getWidth() - this.cpad, 10);
11135         var availWidth = Math.floor(w / vcount);
11136         var b = this.stripBody;
11137         if(b.getWidth() > w){
11138             var tabs = this.items;
11139             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
11140             if(availWidth < this.minTabWidth){
11141                 /*if(!this.sleft){    // incomplete scrolling code
11142                     this.createScrollButtons();
11143                 }
11144                 this.showScroll();
11145                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
11146             }
11147         }else{
11148             if(this.currentTabWidth < this.preferredTabWidth){
11149                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
11150             }
11151         }
11152     },
11153
11154     /**
11155      * Returns the number of tabs in this TabPanel.
11156      * @return {Number}
11157      */
11158      getCount : function(){
11159          return this.items.length;
11160      },
11161
11162     /**
11163      * Resizes all the tabs to the passed width
11164      * @param {Number} The new width
11165      */
11166     setTabWidth : function(width){
11167         this.currentTabWidth = width;
11168         for(var i = 0, len = this.items.length; i < len; i++) {
11169                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
11170         }
11171     },
11172
11173     /**
11174      * Destroys this TabPanel
11175      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
11176      */
11177     destroy : function(removeEl){
11178         Roo.EventManager.removeResizeListener(this.onResize, this);
11179         for(var i = 0, len = this.items.length; i < len; i++){
11180             this.items[i].purgeListeners();
11181         }
11182         if(removeEl === true){
11183             this.el.update("");
11184             this.el.remove();
11185         }
11186     }
11187 });
11188
11189 /**
11190  * @class Roo.TabPanelItem
11191  * @extends Roo.util.Observable
11192  * Represents an individual item (tab plus body) in a TabPanel.
11193  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11194  * @param {String} id The id of this TabPanelItem
11195  * @param {String} text The text for the tab of this TabPanelItem
11196  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11197  */
11198 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11199     /**
11200      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11201      * @type Roo.TabPanel
11202      */
11203     this.tabPanel = tabPanel;
11204     /**
11205      * The id for this TabPanelItem
11206      * @type String
11207      */
11208     this.id = id;
11209     /** @private */
11210     this.disabled = false;
11211     /** @private */
11212     this.text = text;
11213     /** @private */
11214     this.loaded = false;
11215     this.closable = closable;
11216
11217     /**
11218      * The body element for this TabPanelItem.
11219      * @type Roo.Element
11220      */
11221     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11222     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11223     this.bodyEl.setStyle("display", "block");
11224     this.bodyEl.setStyle("zoom", "1");
11225     this.hideAction();
11226
11227     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11228     /** @private */
11229     this.el = Roo.get(els.el, true);
11230     this.inner = Roo.get(els.inner, true);
11231     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11232     this.pnode = Roo.get(els.el.parentNode, true);
11233     this.el.on("mousedown", this.onTabMouseDown, this);
11234     this.el.on("click", this.onTabClick, this);
11235     /** @private */
11236     if(closable){
11237         var c = Roo.get(els.close, true);
11238         c.dom.title = this.closeText;
11239         c.addClassOnOver("close-over");
11240         c.on("click", this.closeClick, this);
11241      }
11242
11243     this.addEvents({
11244          /**
11245          * @event activate
11246          * Fires when this tab becomes the active tab.
11247          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11248          * @param {Roo.TabPanelItem} this
11249          */
11250         "activate": true,
11251         /**
11252          * @event beforeclose
11253          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11254          * @param {Roo.TabPanelItem} this
11255          * @param {Object} e Set cancel to true on this object to cancel the close.
11256          */
11257         "beforeclose": true,
11258         /**
11259          * @event close
11260          * Fires when this tab is closed.
11261          * @param {Roo.TabPanelItem} this
11262          */
11263          "close": true,
11264         /**
11265          * @event deactivate
11266          * Fires when this tab is no longer the active tab.
11267          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11268          * @param {Roo.TabPanelItem} this
11269          */
11270          "deactivate" : true
11271     });
11272     this.hidden = false;
11273
11274     Roo.TabPanelItem.superclass.constructor.call(this);
11275 };
11276
11277 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11278     purgeListeners : function(){
11279        Roo.util.Observable.prototype.purgeListeners.call(this);
11280        this.el.removeAllListeners();
11281     },
11282     /**
11283      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11284      */
11285     show : function(){
11286         this.pnode.addClass("on");
11287         this.showAction();
11288         if(Roo.isOpera){
11289             this.tabPanel.stripWrap.repaint();
11290         }
11291         this.fireEvent("activate", this.tabPanel, this);
11292     },
11293
11294     /**
11295      * Returns true if this tab is the active tab.
11296      * @return {Boolean}
11297      */
11298     isActive : function(){
11299         return this.tabPanel.getActiveTab() == this;
11300     },
11301
11302     /**
11303      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11304      */
11305     hide : function(){
11306         this.pnode.removeClass("on");
11307         this.hideAction();
11308         this.fireEvent("deactivate", this.tabPanel, this);
11309     },
11310
11311     hideAction : function(){
11312         this.bodyEl.hide();
11313         this.bodyEl.setStyle("position", "absolute");
11314         this.bodyEl.setLeft("-20000px");
11315         this.bodyEl.setTop("-20000px");
11316     },
11317
11318     showAction : function(){
11319         this.bodyEl.setStyle("position", "relative");
11320         this.bodyEl.setTop("");
11321         this.bodyEl.setLeft("");
11322         this.bodyEl.show();
11323     },
11324
11325     /**
11326      * Set the tooltip for the tab.
11327      * @param {String} tooltip The tab's tooltip
11328      */
11329     setTooltip : function(text){
11330         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11331             this.textEl.dom.qtip = text;
11332             this.textEl.dom.removeAttribute('title');
11333         }else{
11334             this.textEl.dom.title = text;
11335         }
11336     },
11337
11338     onTabClick : function(e){
11339         e.preventDefault();
11340         this.tabPanel.activate(this.id);
11341     },
11342
11343     onTabMouseDown : function(e){
11344         e.preventDefault();
11345         this.tabPanel.activate(this.id);
11346     },
11347
11348     getWidth : function(){
11349         return this.inner.getWidth();
11350     },
11351
11352     setWidth : function(width){
11353         var iwidth = width - this.pnode.getPadding("lr");
11354         this.inner.setWidth(iwidth);
11355         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11356         this.pnode.setWidth(width);
11357     },
11358
11359     /**
11360      * Show or hide the tab
11361      * @param {Boolean} hidden True to hide or false to show.
11362      */
11363     setHidden : function(hidden){
11364         this.hidden = hidden;
11365         this.pnode.setStyle("display", hidden ? "none" : "");
11366     },
11367
11368     /**
11369      * Returns true if this tab is "hidden"
11370      * @return {Boolean}
11371      */
11372     isHidden : function(){
11373         return this.hidden;
11374     },
11375
11376     /**
11377      * Returns the text for this tab
11378      * @return {String}
11379      */
11380     getText : function(){
11381         return this.text;
11382     },
11383
11384     autoSize : function(){
11385         //this.el.beginMeasure();
11386         this.textEl.setWidth(1);
11387         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11388         //this.el.endMeasure();
11389     },
11390
11391     /**
11392      * Sets the text for the tab (Note: this also sets the tooltip text)
11393      * @param {String} text The tab's text and tooltip
11394      */
11395     setText : function(text){
11396         this.text = text;
11397         this.textEl.update(text);
11398         this.setTooltip(text);
11399         if(!this.tabPanel.resizeTabs){
11400             this.autoSize();
11401         }
11402     },
11403     /**
11404      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11405      */
11406     activate : function(){
11407         this.tabPanel.activate(this.id);
11408     },
11409
11410     /**
11411      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11412      */
11413     disable : function(){
11414         if(this.tabPanel.active != this){
11415             this.disabled = true;
11416             this.pnode.addClass("disabled");
11417         }
11418     },
11419
11420     /**
11421      * Enables this TabPanelItem if it was previously disabled.
11422      */
11423     enable : function(){
11424         this.disabled = false;
11425         this.pnode.removeClass("disabled");
11426     },
11427
11428     /**
11429      * Sets the content for this TabPanelItem.
11430      * @param {String} content The content
11431      * @param {Boolean} loadScripts true to look for and load scripts
11432      */
11433     setContent : function(content, loadScripts){
11434         this.bodyEl.update(content, loadScripts);
11435     },
11436
11437     /**
11438      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11439      * @return {Roo.UpdateManager} The UpdateManager
11440      */
11441     getUpdateManager : function(){
11442         return this.bodyEl.getUpdateManager();
11443     },
11444
11445     /**
11446      * Set a URL to be used to load the content for this TabPanelItem.
11447      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11448      * @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)
11449      * @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)
11450      * @return {Roo.UpdateManager} The UpdateManager
11451      */
11452     setUrl : function(url, params, loadOnce){
11453         if(this.refreshDelegate){
11454             this.un('activate', this.refreshDelegate);
11455         }
11456         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11457         this.on("activate", this.refreshDelegate);
11458         return this.bodyEl.getUpdateManager();
11459     },
11460
11461     /** @private */
11462     _handleRefresh : function(url, params, loadOnce){
11463         if(!loadOnce || !this.loaded){
11464             var updater = this.bodyEl.getUpdateManager();
11465             updater.update(url, params, this._setLoaded.createDelegate(this));
11466         }
11467     },
11468
11469     /**
11470      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11471      *   Will fail silently if the setUrl method has not been called.
11472      *   This does not activate the panel, just updates its content.
11473      */
11474     refresh : function(){
11475         if(this.refreshDelegate){
11476            this.loaded = false;
11477            this.refreshDelegate();
11478         }
11479     },
11480
11481     /** @private */
11482     _setLoaded : function(){
11483         this.loaded = true;
11484     },
11485
11486     /** @private */
11487     closeClick : function(e){
11488         var o = {};
11489         e.stopEvent();
11490         this.fireEvent("beforeclose", this, o);
11491         if(o.cancel !== true){
11492             this.tabPanel.removeTab(this.id);
11493         }
11494     },
11495     /**
11496      * The text displayed in the tooltip for the close icon.
11497      * @type String
11498      */
11499     closeText : "Close this tab"
11500 });
11501
11502 /** @private */
11503 Roo.TabPanel.prototype.createStrip = function(container){
11504     var strip = document.createElement("div");
11505     strip.className = "x-tabs-wrap";
11506     container.appendChild(strip);
11507     return strip;
11508 };
11509 /** @private */
11510 Roo.TabPanel.prototype.createStripList = function(strip){
11511     // div wrapper for retard IE
11512     // returns the "tr" element.
11513     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
11514         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
11515         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
11516     return strip.firstChild.firstChild.firstChild.firstChild;
11517 };
11518 /** @private */
11519 Roo.TabPanel.prototype.createBody = function(container){
11520     var body = document.createElement("div");
11521     Roo.id(body, "tab-body");
11522     Roo.fly(body).addClass("x-tabs-body");
11523     container.appendChild(body);
11524     return body;
11525 };
11526 /** @private */
11527 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11528     var body = Roo.getDom(id);
11529     if(!body){
11530         body = document.createElement("div");
11531         body.id = id;
11532     }
11533     Roo.fly(body).addClass("x-tabs-item-body");
11534     bodyEl.insertBefore(body, bodyEl.firstChild);
11535     return body;
11536 };
11537 /** @private */
11538 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11539     var td = document.createElement("td");
11540     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
11541     //stripEl.appendChild(td);
11542     if(closable){
11543         td.className = "x-tabs-closable";
11544         if(!this.closeTpl){
11545             this.closeTpl = new Roo.Template(
11546                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11547                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11548                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11549             );
11550         }
11551         var el = this.closeTpl.overwrite(td, {"text": text});
11552         var close = el.getElementsByTagName("div")[0];
11553         var inner = el.getElementsByTagName("em")[0];
11554         return {"el": el, "close": close, "inner": inner};
11555     } else {
11556         if(!this.tabTpl){
11557             this.tabTpl = new Roo.Template(
11558                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11559                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11560             );
11561         }
11562         var el = this.tabTpl.overwrite(td, {"text": text});
11563         var inner = el.getElementsByTagName("em")[0];
11564         return {"el": el, "inner": inner};
11565     }
11566 };/*
11567  * Based on:
11568  * Ext JS Library 1.1.1
11569  * Copyright(c) 2006-2007, Ext JS, LLC.
11570  *
11571  * Originally Released Under LGPL - original licence link has changed is not relivant.
11572  *
11573  * Fork - LGPL
11574  * <script type="text/javascript">
11575  */
11576
11577 /**
11578  * @class Roo.Button
11579  * @extends Roo.util.Observable
11580  * Simple Button class
11581  * @cfg {String} text The button text
11582  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11583  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11584  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11585  * @cfg {Object} scope The scope of the handler
11586  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11587  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11588  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11589  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11590  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11591  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11592    applies if enableToggle = true)
11593  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11594  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11595   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11596  * @constructor
11597  * Create a new button
11598  * @param {Object} config The config object
11599  */
11600 Roo.Button = function(renderTo, config)
11601 {
11602     if (!config) {
11603         config = renderTo;
11604         renderTo = config.renderTo || false;
11605     }
11606     
11607     Roo.apply(this, config);
11608     this.addEvents({
11609         /**
11610              * @event click
11611              * Fires when this button is clicked
11612              * @param {Button} this
11613              * @param {EventObject} e The click event
11614              */
11615             "click" : true,
11616         /**
11617              * @event toggle
11618              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11619              * @param {Button} this
11620              * @param {Boolean} pressed
11621              */
11622             "toggle" : true,
11623         /**
11624              * @event mouseover
11625              * Fires when the mouse hovers over the button
11626              * @param {Button} this
11627              * @param {Event} e The event object
11628              */
11629         'mouseover' : true,
11630         /**
11631              * @event mouseout
11632              * Fires when the mouse exits the button
11633              * @param {Button} this
11634              * @param {Event} e The event object
11635              */
11636         'mouseout': true,
11637          /**
11638              * @event render
11639              * Fires when the button is rendered
11640              * @param {Button} this
11641              */
11642         'render': true
11643     });
11644     if(this.menu){
11645         this.menu = Roo.menu.MenuMgr.get(this.menu);
11646     }
11647     // register listeners first!!  - so render can be captured..
11648     Roo.util.Observable.call(this);
11649     if(renderTo){
11650         this.render(renderTo);
11651     }
11652     
11653   
11654 };
11655
11656 Roo.extend(Roo.Button, Roo.util.Observable, {
11657     /**
11658      * 
11659      */
11660     
11661     /**
11662      * Read-only. True if this button is hidden
11663      * @type Boolean
11664      */
11665     hidden : false,
11666     /**
11667      * Read-only. True if this button is disabled
11668      * @type Boolean
11669      */
11670     disabled : false,
11671     /**
11672      * Read-only. True if this button is pressed (only if enableToggle = true)
11673      * @type Boolean
11674      */
11675     pressed : false,
11676
11677     /**
11678      * @cfg {Number} tabIndex 
11679      * The DOM tabIndex for this button (defaults to undefined)
11680      */
11681     tabIndex : undefined,
11682
11683     /**
11684      * @cfg {Boolean} enableToggle
11685      * True to enable pressed/not pressed toggling (defaults to false)
11686      */
11687     enableToggle: false,
11688     /**
11689      * @cfg {Mixed} menu
11690      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11691      */
11692     menu : undefined,
11693     /**
11694      * @cfg {String} menuAlign
11695      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11696      */
11697     menuAlign : "tl-bl?",
11698
11699     /**
11700      * @cfg {String} iconCls
11701      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11702      */
11703     iconCls : undefined,
11704     /**
11705      * @cfg {String} type
11706      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11707      */
11708     type : 'button',
11709
11710     // private
11711     menuClassTarget: 'tr',
11712
11713     /**
11714      * @cfg {String} clickEvent
11715      * The type of event to map to the button's event handler (defaults to 'click')
11716      */
11717     clickEvent : 'click',
11718
11719     /**
11720      * @cfg {Boolean} handleMouseEvents
11721      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11722      */
11723     handleMouseEvents : true,
11724
11725     /**
11726      * @cfg {String} tooltipType
11727      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11728      */
11729     tooltipType : 'qtip',
11730
11731     /**
11732      * @cfg {String} cls
11733      * A CSS class to apply to the button's main element.
11734      */
11735     
11736     /**
11737      * @cfg {Roo.Template} template (Optional)
11738      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11739      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11740      * require code modifications if required elements (e.g. a button) aren't present.
11741      */
11742
11743     // private
11744     render : function(renderTo){
11745         var btn;
11746         if(this.hideParent){
11747             this.parentEl = Roo.get(renderTo);
11748         }
11749         if(!this.dhconfig){
11750             if(!this.template){
11751                 if(!Roo.Button.buttonTemplate){
11752                     // hideous table template
11753                     Roo.Button.buttonTemplate = new Roo.Template(
11754                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11755                         '<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>',
11756                         "</tr></tbody></table>");
11757                 }
11758                 this.template = Roo.Button.buttonTemplate;
11759             }
11760             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11761             var btnEl = btn.child("button:first");
11762             btnEl.on('focus', this.onFocus, this);
11763             btnEl.on('blur', this.onBlur, this);
11764             if(this.cls){
11765                 btn.addClass(this.cls);
11766             }
11767             if(this.icon){
11768                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11769             }
11770             if(this.iconCls){
11771                 btnEl.addClass(this.iconCls);
11772                 if(!this.cls){
11773                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11774                 }
11775             }
11776             if(this.tabIndex !== undefined){
11777                 btnEl.dom.tabIndex = this.tabIndex;
11778             }
11779             if(this.tooltip){
11780                 if(typeof this.tooltip == 'object'){
11781                     Roo.QuickTips.tips(Roo.apply({
11782                           target: btnEl.id
11783                     }, this.tooltip));
11784                 } else {
11785                     btnEl.dom[this.tooltipType] = this.tooltip;
11786                 }
11787             }
11788         }else{
11789             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11790         }
11791         this.el = btn;
11792         if(this.id){
11793             this.el.dom.id = this.el.id = this.id;
11794         }
11795         if(this.menu){
11796             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11797             this.menu.on("show", this.onMenuShow, this);
11798             this.menu.on("hide", this.onMenuHide, this);
11799         }
11800         btn.addClass("x-btn");
11801         if(Roo.isIE && !Roo.isIE7){
11802             this.autoWidth.defer(1, this);
11803         }else{
11804             this.autoWidth();
11805         }
11806         if(this.handleMouseEvents){
11807             btn.on("mouseover", this.onMouseOver, this);
11808             btn.on("mouseout", this.onMouseOut, this);
11809             btn.on("mousedown", this.onMouseDown, this);
11810         }
11811         btn.on(this.clickEvent, this.onClick, this);
11812         //btn.on("mouseup", this.onMouseUp, this);
11813         if(this.hidden){
11814             this.hide();
11815         }
11816         if(this.disabled){
11817             this.disable();
11818         }
11819         Roo.ButtonToggleMgr.register(this);
11820         if(this.pressed){
11821             this.el.addClass("x-btn-pressed");
11822         }
11823         if(this.repeat){
11824             var repeater = new Roo.util.ClickRepeater(btn,
11825                 typeof this.repeat == "object" ? this.repeat : {}
11826             );
11827             repeater.on("click", this.onClick,  this);
11828         }
11829         
11830         this.fireEvent('render', this);
11831         
11832     },
11833     /**
11834      * Returns the button's underlying element
11835      * @return {Roo.Element} The element
11836      */
11837     getEl : function(){
11838         return this.el;  
11839     },
11840     
11841     /**
11842      * Destroys this Button and removes any listeners.
11843      */
11844     destroy : function(){
11845         Roo.ButtonToggleMgr.unregister(this);
11846         this.el.removeAllListeners();
11847         this.purgeListeners();
11848         this.el.remove();
11849     },
11850
11851     // private
11852     autoWidth : function(){
11853         if(this.el){
11854             this.el.setWidth("auto");
11855             if(Roo.isIE7 && Roo.isStrict){
11856                 var ib = this.el.child('button');
11857                 if(ib && ib.getWidth() > 20){
11858                     ib.clip();
11859                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11860                 }
11861             }
11862             if(this.minWidth){
11863                 if(this.hidden){
11864                     this.el.beginMeasure();
11865                 }
11866                 if(this.el.getWidth() < this.minWidth){
11867                     this.el.setWidth(this.minWidth);
11868                 }
11869                 if(this.hidden){
11870                     this.el.endMeasure();
11871                 }
11872             }
11873         }
11874     },
11875
11876     /**
11877      * Assigns this button's click handler
11878      * @param {Function} handler The function to call when the button is clicked
11879      * @param {Object} scope (optional) Scope for the function passed in
11880      */
11881     setHandler : function(handler, scope){
11882         this.handler = handler;
11883         this.scope = scope;  
11884     },
11885     
11886     /**
11887      * Sets this button's text
11888      * @param {String} text The button text
11889      */
11890     setText : function(text){
11891         this.text = text;
11892         if(this.el){
11893             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11894         }
11895         this.autoWidth();
11896     },
11897     
11898     /**
11899      * Gets the text for this button
11900      * @return {String} The button text
11901      */
11902     getText : function(){
11903         return this.text;  
11904     },
11905     
11906     /**
11907      * Show this button
11908      */
11909     show: function(){
11910         this.hidden = false;
11911         if(this.el){
11912             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11913         }
11914     },
11915     
11916     /**
11917      * Hide this button
11918      */
11919     hide: function(){
11920         this.hidden = true;
11921         if(this.el){
11922             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11923         }
11924     },
11925     
11926     /**
11927      * Convenience function for boolean show/hide
11928      * @param {Boolean} visible True to show, false to hide
11929      */
11930     setVisible: function(visible){
11931         if(visible) {
11932             this.show();
11933         }else{
11934             this.hide();
11935         }
11936     },
11937     
11938     /**
11939      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11940      * @param {Boolean} state (optional) Force a particular state
11941      */
11942     toggle : function(state){
11943         state = state === undefined ? !this.pressed : state;
11944         if(state != this.pressed){
11945             if(state){
11946                 this.el.addClass("x-btn-pressed");
11947                 this.pressed = true;
11948                 this.fireEvent("toggle", this, true);
11949             }else{
11950                 this.el.removeClass("x-btn-pressed");
11951                 this.pressed = false;
11952                 this.fireEvent("toggle", this, false);
11953             }
11954             if(this.toggleHandler){
11955                 this.toggleHandler.call(this.scope || this, this, state);
11956             }
11957         }
11958     },
11959     
11960     /**
11961      * Focus the button
11962      */
11963     focus : function(){
11964         this.el.child('button:first').focus();
11965     },
11966     
11967     /**
11968      * Disable this button
11969      */
11970     disable : function(){
11971         if(this.el){
11972             this.el.addClass("x-btn-disabled");
11973         }
11974         this.disabled = true;
11975     },
11976     
11977     /**
11978      * Enable this button
11979      */
11980     enable : function(){
11981         if(this.el){
11982             this.el.removeClass("x-btn-disabled");
11983         }
11984         this.disabled = false;
11985     },
11986
11987     /**
11988      * Convenience function for boolean enable/disable
11989      * @param {Boolean} enabled True to enable, false to disable
11990      */
11991     setDisabled : function(v){
11992         this[v !== true ? "enable" : "disable"]();
11993     },
11994
11995     // private
11996     onClick : function(e){
11997         if(e){
11998             e.preventDefault();
11999         }
12000         if(e.button != 0){
12001             return;
12002         }
12003         if(!this.disabled){
12004             if(this.enableToggle){
12005                 this.toggle();
12006             }
12007             if(this.menu && !this.menu.isVisible()){
12008                 this.menu.show(this.el, this.menuAlign);
12009             }
12010             this.fireEvent("click", this, e);
12011             if(this.handler){
12012                 this.el.removeClass("x-btn-over");
12013                 this.handler.call(this.scope || this, this, e);
12014             }
12015         }
12016     },
12017     // private
12018     onMouseOver : function(e){
12019         if(!this.disabled){
12020             this.el.addClass("x-btn-over");
12021             this.fireEvent('mouseover', this, e);
12022         }
12023     },
12024     // private
12025     onMouseOut : function(e){
12026         if(!e.within(this.el,  true)){
12027             this.el.removeClass("x-btn-over");
12028             this.fireEvent('mouseout', this, e);
12029         }
12030     },
12031     // private
12032     onFocus : function(e){
12033         if(!this.disabled){
12034             this.el.addClass("x-btn-focus");
12035         }
12036     },
12037     // private
12038     onBlur : function(e){
12039         this.el.removeClass("x-btn-focus");
12040     },
12041     // private
12042     onMouseDown : function(e){
12043         if(!this.disabled && e.button == 0){
12044             this.el.addClass("x-btn-click");
12045             Roo.get(document).on('mouseup', this.onMouseUp, this);
12046         }
12047     },
12048     // private
12049     onMouseUp : function(e){
12050         if(e.button == 0){
12051             this.el.removeClass("x-btn-click");
12052             Roo.get(document).un('mouseup', this.onMouseUp, this);
12053         }
12054     },
12055     // private
12056     onMenuShow : function(e){
12057         this.el.addClass("x-btn-menu-active");
12058     },
12059     // private
12060     onMenuHide : function(e){
12061         this.el.removeClass("x-btn-menu-active");
12062     }   
12063 });
12064
12065 // Private utility class used by Button
12066 Roo.ButtonToggleMgr = function(){
12067    var groups = {};
12068    
12069    function toggleGroup(btn, state){
12070        if(state){
12071            var g = groups[btn.toggleGroup];
12072            for(var i = 0, l = g.length; i < l; i++){
12073                if(g[i] != btn){
12074                    g[i].toggle(false);
12075                }
12076            }
12077        }
12078    }
12079    
12080    return {
12081        register : function(btn){
12082            if(!btn.toggleGroup){
12083                return;
12084            }
12085            var g = groups[btn.toggleGroup];
12086            if(!g){
12087                g = groups[btn.toggleGroup] = [];
12088            }
12089            g.push(btn);
12090            btn.on("toggle", toggleGroup);
12091        },
12092        
12093        unregister : function(btn){
12094            if(!btn.toggleGroup){
12095                return;
12096            }
12097            var g = groups[btn.toggleGroup];
12098            if(g){
12099                g.remove(btn);
12100                btn.un("toggle", toggleGroup);
12101            }
12102        }
12103    };
12104 }();/*
12105  * Based on:
12106  * Ext JS Library 1.1.1
12107  * Copyright(c) 2006-2007, Ext JS, LLC.
12108  *
12109  * Originally Released Under LGPL - original licence link has changed is not relivant.
12110  *
12111  * Fork - LGPL
12112  * <script type="text/javascript">
12113  */
12114  
12115 /**
12116  * @class Roo.SplitButton
12117  * @extends Roo.Button
12118  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
12119  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
12120  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
12121  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
12122  * @cfg {String} arrowTooltip The title attribute of the arrow
12123  * @constructor
12124  * Create a new menu button
12125  * @param {String/HTMLElement/Element} renderTo The element to append the button to
12126  * @param {Object} config The config object
12127  */
12128 Roo.SplitButton = function(renderTo, config){
12129     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
12130     /**
12131      * @event arrowclick
12132      * Fires when this button's arrow is clicked
12133      * @param {SplitButton} this
12134      * @param {EventObject} e The click event
12135      */
12136     this.addEvents({"arrowclick":true});
12137 };
12138
12139 Roo.extend(Roo.SplitButton, Roo.Button, {
12140     render : function(renderTo){
12141         // this is one sweet looking template!
12142         var tpl = new Roo.Template(
12143             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
12144             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
12145             '<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>',
12146             "</tbody></table></td><td>",
12147             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
12148             '<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>',
12149             "</tbody></table></td></tr></table>"
12150         );
12151         var btn = tpl.append(renderTo, [this.text, this.type], true);
12152         var btnEl = btn.child("button");
12153         if(this.cls){
12154             btn.addClass(this.cls);
12155         }
12156         if(this.icon){
12157             btnEl.setStyle('background-image', 'url(' +this.icon +')');
12158         }
12159         if(this.iconCls){
12160             btnEl.addClass(this.iconCls);
12161             if(!this.cls){
12162                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
12163             }
12164         }
12165         this.el = btn;
12166         if(this.handleMouseEvents){
12167             btn.on("mouseover", this.onMouseOver, this);
12168             btn.on("mouseout", this.onMouseOut, this);
12169             btn.on("mousedown", this.onMouseDown, this);
12170             btn.on("mouseup", this.onMouseUp, this);
12171         }
12172         btn.on(this.clickEvent, this.onClick, this);
12173         if(this.tooltip){
12174             if(typeof this.tooltip == 'object'){
12175                 Roo.QuickTips.tips(Roo.apply({
12176                       target: btnEl.id
12177                 }, this.tooltip));
12178             } else {
12179                 btnEl.dom[this.tooltipType] = this.tooltip;
12180             }
12181         }
12182         if(this.arrowTooltip){
12183             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
12184         }
12185         if(this.hidden){
12186             this.hide();
12187         }
12188         if(this.disabled){
12189             this.disable();
12190         }
12191         if(this.pressed){
12192             this.el.addClass("x-btn-pressed");
12193         }
12194         if(Roo.isIE && !Roo.isIE7){
12195             this.autoWidth.defer(1, this);
12196         }else{
12197             this.autoWidth();
12198         }
12199         if(this.menu){
12200             this.menu.on("show", this.onMenuShow, this);
12201             this.menu.on("hide", this.onMenuHide, this);
12202         }
12203         this.fireEvent('render', this);
12204     },
12205
12206     // private
12207     autoWidth : function(){
12208         if(this.el){
12209             var tbl = this.el.child("table:first");
12210             var tbl2 = this.el.child("table:last");
12211             this.el.setWidth("auto");
12212             tbl.setWidth("auto");
12213             if(Roo.isIE7 && Roo.isStrict){
12214                 var ib = this.el.child('button:first');
12215                 if(ib && ib.getWidth() > 20){
12216                     ib.clip();
12217                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12218                 }
12219             }
12220             if(this.minWidth){
12221                 if(this.hidden){
12222                     this.el.beginMeasure();
12223                 }
12224                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12225                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12226                 }
12227                 if(this.hidden){
12228                     this.el.endMeasure();
12229                 }
12230             }
12231             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12232         } 
12233     },
12234     /**
12235      * Sets this button's click handler
12236      * @param {Function} handler The function to call when the button is clicked
12237      * @param {Object} scope (optional) Scope for the function passed above
12238      */
12239     setHandler : function(handler, scope){
12240         this.handler = handler;
12241         this.scope = scope;  
12242     },
12243     
12244     /**
12245      * Sets this button's arrow click handler
12246      * @param {Function} handler The function to call when the arrow is clicked
12247      * @param {Object} scope (optional) Scope for the function passed above
12248      */
12249     setArrowHandler : function(handler, scope){
12250         this.arrowHandler = handler;
12251         this.scope = scope;  
12252     },
12253     
12254     /**
12255      * Focus the button
12256      */
12257     focus : function(){
12258         if(this.el){
12259             this.el.child("button:first").focus();
12260         }
12261     },
12262
12263     // private
12264     onClick : function(e){
12265         e.preventDefault();
12266         if(!this.disabled){
12267             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12268                 if(this.menu && !this.menu.isVisible()){
12269                     this.menu.show(this.el, this.menuAlign);
12270                 }
12271                 this.fireEvent("arrowclick", this, e);
12272                 if(this.arrowHandler){
12273                     this.arrowHandler.call(this.scope || this, this, e);
12274                 }
12275             }else{
12276                 this.fireEvent("click", this, e);
12277                 if(this.handler){
12278                     this.handler.call(this.scope || this, this, e);
12279                 }
12280             }
12281         }
12282     },
12283     // private
12284     onMouseDown : function(e){
12285         if(!this.disabled){
12286             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12287         }
12288     },
12289     // private
12290     onMouseUp : function(e){
12291         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12292     }   
12293 });
12294
12295
12296 // backwards compat
12297 Roo.MenuButton = Roo.SplitButton;/*
12298  * Based on:
12299  * Ext JS Library 1.1.1
12300  * Copyright(c) 2006-2007, Ext JS, LLC.
12301  *
12302  * Originally Released Under LGPL - original licence link has changed is not relivant.
12303  *
12304  * Fork - LGPL
12305  * <script type="text/javascript">
12306  */
12307
12308 /**
12309  * @class Roo.Toolbar
12310  * Basic Toolbar class.
12311  * @constructor
12312  * Creates a new Toolbar
12313  * @param {Object} container The config object
12314  */ 
12315 Roo.Toolbar = function(container, buttons, config)
12316 {
12317     /// old consturctor format still supported..
12318     if(container instanceof Array){ // omit the container for later rendering
12319         buttons = container;
12320         config = buttons;
12321         container = null;
12322     }
12323     if (typeof(container) == 'object' && container.xtype) {
12324         config = container;
12325         container = config.container;
12326         buttons = config.buttons || []; // not really - use items!!
12327     }
12328     var xitems = [];
12329     if (config && config.items) {
12330         xitems = config.items;
12331         delete config.items;
12332     }
12333     Roo.apply(this, config);
12334     this.buttons = buttons;
12335     
12336     if(container){
12337         this.render(container);
12338     }
12339     this.xitems = xitems;
12340     Roo.each(xitems, function(b) {
12341         this.add(b);
12342     }, this);
12343     
12344 };
12345
12346 Roo.Toolbar.prototype = {
12347     /**
12348      * @cfg {Array} items
12349      * array of button configs or elements to add (will be converted to a MixedCollection)
12350      */
12351     
12352     /**
12353      * @cfg {String/HTMLElement/Element} container
12354      * The id or element that will contain the toolbar
12355      */
12356     // private
12357     render : function(ct){
12358         this.el = Roo.get(ct);
12359         if(this.cls){
12360             this.el.addClass(this.cls);
12361         }
12362         // using a table allows for vertical alignment
12363         // 100% width is needed by Safari...
12364         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12365         this.tr = this.el.child("tr", true);
12366         var autoId = 0;
12367         this.items = new Roo.util.MixedCollection(false, function(o){
12368             return o.id || ("item" + (++autoId));
12369         });
12370         if(this.buttons){
12371             this.add.apply(this, this.buttons);
12372             delete this.buttons;
12373         }
12374     },
12375
12376     /**
12377      * Adds element(s) to the toolbar -- this function takes a variable number of 
12378      * arguments of mixed type and adds them to the toolbar.
12379      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12380      * <ul>
12381      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12382      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12383      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12384      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12385      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12386      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12387      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12388      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12389      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12390      * </ul>
12391      * @param {Mixed} arg2
12392      * @param {Mixed} etc.
12393      */
12394     add : function(){
12395         var a = arguments, l = a.length;
12396         for(var i = 0; i < l; i++){
12397             this._add(a[i]);
12398         }
12399     },
12400     // private..
12401     _add : function(el) {
12402         
12403         if (el.xtype) {
12404             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12405         }
12406         
12407         if (el.applyTo){ // some kind of form field
12408             return this.addField(el);
12409         } 
12410         if (el.render){ // some kind of Toolbar.Item
12411             return this.addItem(el);
12412         }
12413         if (typeof el == "string"){ // string
12414             if(el == "separator" || el == "-"){
12415                 return this.addSeparator();
12416             }
12417             if (el == " "){
12418                 return this.addSpacer();
12419             }
12420             if(el == "->"){
12421                 return this.addFill();
12422             }
12423             return this.addText(el);
12424             
12425         }
12426         if(el.tagName){ // element
12427             return this.addElement(el);
12428         }
12429         if(typeof el == "object"){ // must be button config?
12430             return this.addButton(el);
12431         }
12432         // and now what?!?!
12433         return false;
12434         
12435     },
12436     
12437     /**
12438      * Add an Xtype element
12439      * @param {Object} xtype Xtype Object
12440      * @return {Object} created Object
12441      */
12442     addxtype : function(e){
12443         return this.add(e);  
12444     },
12445     
12446     /**
12447      * Returns the Element for this toolbar.
12448      * @return {Roo.Element}
12449      */
12450     getEl : function(){
12451         return this.el;  
12452     },
12453     
12454     /**
12455      * Adds a separator
12456      * @return {Roo.Toolbar.Item} The separator item
12457      */
12458     addSeparator : function(){
12459         return this.addItem(new Roo.Toolbar.Separator());
12460     },
12461
12462     /**
12463      * Adds a spacer element
12464      * @return {Roo.Toolbar.Spacer} The spacer item
12465      */
12466     addSpacer : function(){
12467         return this.addItem(new Roo.Toolbar.Spacer());
12468     },
12469
12470     /**
12471      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12472      * @return {Roo.Toolbar.Fill} The fill item
12473      */
12474     addFill : function(){
12475         return this.addItem(new Roo.Toolbar.Fill());
12476     },
12477
12478     /**
12479      * Adds any standard HTML element to the toolbar
12480      * @param {String/HTMLElement/Element} el The element or id of the element to add
12481      * @return {Roo.Toolbar.Item} The element's item
12482      */
12483     addElement : function(el){
12484         return this.addItem(new Roo.Toolbar.Item(el));
12485     },
12486     /**
12487      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12488      * @type Roo.util.MixedCollection  
12489      */
12490     items : false,
12491      
12492     /**
12493      * Adds any Toolbar.Item or subclass
12494      * @param {Roo.Toolbar.Item} item
12495      * @return {Roo.Toolbar.Item} The item
12496      */
12497     addItem : function(item){
12498         var td = this.nextBlock();
12499         item.render(td);
12500         this.items.add(item);
12501         return item;
12502     },
12503     
12504     /**
12505      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12506      * @param {Object/Array} config A button config or array of configs
12507      * @return {Roo.Toolbar.Button/Array}
12508      */
12509     addButton : function(config){
12510         if(config instanceof Array){
12511             var buttons = [];
12512             for(var i = 0, len = config.length; i < len; i++) {
12513                 buttons.push(this.addButton(config[i]));
12514             }
12515             return buttons;
12516         }
12517         var b = config;
12518         if(!(config instanceof Roo.Toolbar.Button)){
12519             b = config.split ?
12520                 new Roo.Toolbar.SplitButton(config) :
12521                 new Roo.Toolbar.Button(config);
12522         }
12523         var td = this.nextBlock();
12524         b.render(td);
12525         this.items.add(b);
12526         return b;
12527     },
12528     
12529     /**
12530      * Adds text to the toolbar
12531      * @param {String} text The text to add
12532      * @return {Roo.Toolbar.Item} The element's item
12533      */
12534     addText : function(text){
12535         return this.addItem(new Roo.Toolbar.TextItem(text));
12536     },
12537     
12538     /**
12539      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12540      * @param {Number} index The index where the item is to be inserted
12541      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12542      * @return {Roo.Toolbar.Button/Item}
12543      */
12544     insertButton : function(index, item){
12545         if(item instanceof Array){
12546             var buttons = [];
12547             for(var i = 0, len = item.length; i < len; i++) {
12548                buttons.push(this.insertButton(index + i, item[i]));
12549             }
12550             return buttons;
12551         }
12552         if (!(item instanceof Roo.Toolbar.Button)){
12553            item = new Roo.Toolbar.Button(item);
12554         }
12555         var td = document.createElement("td");
12556         this.tr.insertBefore(td, this.tr.childNodes[index]);
12557         item.render(td);
12558         this.items.insert(index, item);
12559         return item;
12560     },
12561     
12562     /**
12563      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12564      * @param {Object} config
12565      * @return {Roo.Toolbar.Item} The element's item
12566      */
12567     addDom : function(config, returnEl){
12568         var td = this.nextBlock();
12569         Roo.DomHelper.overwrite(td, config);
12570         var ti = new Roo.Toolbar.Item(td.firstChild);
12571         ti.render(td);
12572         this.items.add(ti);
12573         return ti;
12574     },
12575
12576     /**
12577      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12578      * @type Roo.util.MixedCollection  
12579      */
12580     fields : false,
12581     
12582     /**
12583      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
12584      * Note: the field should not have been rendered yet. For a field that has already been
12585      * rendered, use {@link #addElement}.
12586      * @param {Roo.form.Field} field
12587      * @return {Roo.ToolbarItem}
12588      */
12589      
12590       
12591     addField : function(field) {
12592         if (!this.fields) {
12593             var autoId = 0;
12594             this.fields = new Roo.util.MixedCollection(false, function(o){
12595                 return o.id || ("item" + (++autoId));
12596             });
12597
12598         }
12599         
12600         var td = this.nextBlock();
12601         field.render(td);
12602         var ti = new Roo.Toolbar.Item(td.firstChild);
12603         ti.render(td);
12604         this.items.add(ti);
12605         this.fields.add(field);
12606         return ti;
12607     },
12608     /**
12609      * Hide the toolbar
12610      * @method hide
12611      */
12612      
12613       
12614     hide : function()
12615     {
12616         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12617         this.el.child('div').hide();
12618     },
12619     /**
12620      * Show the toolbar
12621      * @method show
12622      */
12623     show : function()
12624     {
12625         this.el.child('div').show();
12626     },
12627       
12628     // private
12629     nextBlock : function(){
12630         var td = document.createElement("td");
12631         this.tr.appendChild(td);
12632         return td;
12633     },
12634
12635     // private
12636     destroy : function(){
12637         if(this.items){ // rendered?
12638             Roo.destroy.apply(Roo, this.items.items);
12639         }
12640         if(this.fields){ // rendered?
12641             Roo.destroy.apply(Roo, this.fields.items);
12642         }
12643         Roo.Element.uncache(this.el, this.tr);
12644     }
12645 };
12646
12647 /**
12648  * @class Roo.Toolbar.Item
12649  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12650  * @constructor
12651  * Creates a new Item
12652  * @param {HTMLElement} el 
12653  */
12654 Roo.Toolbar.Item = function(el){
12655     this.el = Roo.getDom(el);
12656     this.id = Roo.id(this.el);
12657     this.hidden = false;
12658 };
12659
12660 Roo.Toolbar.Item.prototype = {
12661     
12662     /**
12663      * Get this item's HTML Element
12664      * @return {HTMLElement}
12665      */
12666     getEl : function(){
12667        return this.el;  
12668     },
12669
12670     // private
12671     render : function(td){
12672         this.td = td;
12673         td.appendChild(this.el);
12674     },
12675     
12676     /**
12677      * Removes and destroys this item.
12678      */
12679     destroy : function(){
12680         this.td.parentNode.removeChild(this.td);
12681     },
12682     
12683     /**
12684      * Shows this item.
12685      */
12686     show: function(){
12687         this.hidden = false;
12688         this.td.style.display = "";
12689     },
12690     
12691     /**
12692      * Hides this item.
12693      */
12694     hide: function(){
12695         this.hidden = true;
12696         this.td.style.display = "none";
12697     },
12698     
12699     /**
12700      * Convenience function for boolean show/hide.
12701      * @param {Boolean} visible true to show/false to hide
12702      */
12703     setVisible: function(visible){
12704         if(visible) {
12705             this.show();
12706         }else{
12707             this.hide();
12708         }
12709     },
12710     
12711     /**
12712      * Try to focus this item.
12713      */
12714     focus : function(){
12715         Roo.fly(this.el).focus();
12716     },
12717     
12718     /**
12719      * Disables this item.
12720      */
12721     disable : function(){
12722         Roo.fly(this.td).addClass("x-item-disabled");
12723         this.disabled = true;
12724         this.el.disabled = true;
12725     },
12726     
12727     /**
12728      * Enables this item.
12729      */
12730     enable : function(){
12731         Roo.fly(this.td).removeClass("x-item-disabled");
12732         this.disabled = false;
12733         this.el.disabled = false;
12734     }
12735 };
12736
12737
12738 /**
12739  * @class Roo.Toolbar.Separator
12740  * @extends Roo.Toolbar.Item
12741  * A simple toolbar separator class
12742  * @constructor
12743  * Creates a new Separator
12744  */
12745 Roo.Toolbar.Separator = function(){
12746     var s = document.createElement("span");
12747     s.className = "ytb-sep";
12748     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12749 };
12750 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12751     enable:Roo.emptyFn,
12752     disable:Roo.emptyFn,
12753     focus:Roo.emptyFn
12754 });
12755
12756 /**
12757  * @class Roo.Toolbar.Spacer
12758  * @extends Roo.Toolbar.Item
12759  * A simple element that adds extra horizontal space to a toolbar.
12760  * @constructor
12761  * Creates a new Spacer
12762  */
12763 Roo.Toolbar.Spacer = function(){
12764     var s = document.createElement("div");
12765     s.className = "ytb-spacer";
12766     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12767 };
12768 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12769     enable:Roo.emptyFn,
12770     disable:Roo.emptyFn,
12771     focus:Roo.emptyFn
12772 });
12773
12774 /**
12775  * @class Roo.Toolbar.Fill
12776  * @extends Roo.Toolbar.Spacer
12777  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12778  * @constructor
12779  * Creates a new Spacer
12780  */
12781 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12782     // private
12783     render : function(td){
12784         td.style.width = '100%';
12785         Roo.Toolbar.Fill.superclass.render.call(this, td);
12786     }
12787 });
12788
12789 /**
12790  * @class Roo.Toolbar.TextItem
12791  * @extends Roo.Toolbar.Item
12792  * A simple class that renders text directly into a toolbar.
12793  * @constructor
12794  * Creates a new TextItem
12795  * @param {String} text
12796  */
12797 Roo.Toolbar.TextItem = function(text){
12798     if (typeof(text) == 'object') {
12799         text = text.text;
12800     }
12801     var s = document.createElement("span");
12802     s.className = "ytb-text";
12803     s.innerHTML = text;
12804     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12805 };
12806 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12807     enable:Roo.emptyFn,
12808     disable:Roo.emptyFn,
12809     focus:Roo.emptyFn
12810 });
12811
12812 /**
12813  * @class Roo.Toolbar.Button
12814  * @extends Roo.Button
12815  * A button that renders into a toolbar.
12816  * @constructor
12817  * Creates a new Button
12818  * @param {Object} config A standard {@link Roo.Button} config object
12819  */
12820 Roo.Toolbar.Button = function(config){
12821     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12822 };
12823 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12824     render : function(td){
12825         this.td = td;
12826         Roo.Toolbar.Button.superclass.render.call(this, td);
12827     },
12828     
12829     /**
12830      * Removes and destroys this button
12831      */
12832     destroy : function(){
12833         Roo.Toolbar.Button.superclass.destroy.call(this);
12834         this.td.parentNode.removeChild(this.td);
12835     },
12836     
12837     /**
12838      * Shows this button
12839      */
12840     show: function(){
12841         this.hidden = false;
12842         this.td.style.display = "";
12843     },
12844     
12845     /**
12846      * Hides this button
12847      */
12848     hide: function(){
12849         this.hidden = true;
12850         this.td.style.display = "none";
12851     },
12852
12853     /**
12854      * Disables this item
12855      */
12856     disable : function(){
12857         Roo.fly(this.td).addClass("x-item-disabled");
12858         this.disabled = true;
12859     },
12860
12861     /**
12862      * Enables this item
12863      */
12864     enable : function(){
12865         Roo.fly(this.td).removeClass("x-item-disabled");
12866         this.disabled = false;
12867     }
12868 });
12869 // backwards compat
12870 Roo.ToolbarButton = Roo.Toolbar.Button;
12871
12872 /**
12873  * @class Roo.Toolbar.SplitButton
12874  * @extends Roo.SplitButton
12875  * A menu button that renders into a toolbar.
12876  * @constructor
12877  * Creates a new SplitButton
12878  * @param {Object} config A standard {@link Roo.SplitButton} config object
12879  */
12880 Roo.Toolbar.SplitButton = function(config){
12881     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12882 };
12883 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12884     render : function(td){
12885         this.td = td;
12886         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12887     },
12888     
12889     /**
12890      * Removes and destroys this button
12891      */
12892     destroy : function(){
12893         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12894         this.td.parentNode.removeChild(this.td);
12895     },
12896     
12897     /**
12898      * Shows this button
12899      */
12900     show: function(){
12901         this.hidden = false;
12902         this.td.style.display = "";
12903     },
12904     
12905     /**
12906      * Hides this button
12907      */
12908     hide: function(){
12909         this.hidden = true;
12910         this.td.style.display = "none";
12911     }
12912 });
12913
12914 // backwards compat
12915 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12916  * Based on:
12917  * Ext JS Library 1.1.1
12918  * Copyright(c) 2006-2007, Ext JS, LLC.
12919  *
12920  * Originally Released Under LGPL - original licence link has changed is not relivant.
12921  *
12922  * Fork - LGPL
12923  * <script type="text/javascript">
12924  */
12925  
12926 /**
12927  * @class Roo.PagingToolbar
12928  * @extends Roo.Toolbar
12929  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12930  * @constructor
12931  * Create a new PagingToolbar
12932  * @param {Object} config The config object
12933  */
12934 Roo.PagingToolbar = function(el, ds, config)
12935 {
12936     // old args format still supported... - xtype is prefered..
12937     if (typeof(el) == 'object' && el.xtype) {
12938         // created from xtype...
12939         config = el;
12940         ds = el.dataSource;
12941         el = config.container;
12942     }
12943     var items = [];
12944     if (config.items) {
12945         items = config.items;
12946         config.items = [];
12947     }
12948     
12949     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12950     this.ds = ds;
12951     this.cursor = 0;
12952     this.renderButtons(this.el);
12953     this.bind(ds);
12954     
12955     // supprot items array.
12956    
12957     Roo.each(items, function(e) {
12958         this.add(Roo.factory(e));
12959     },this);
12960     
12961 };
12962
12963 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12964     /**
12965      * @cfg {Roo.data.Store} dataSource
12966      * The underlying data store providing the paged data
12967      */
12968     /**
12969      * @cfg {String/HTMLElement/Element} container
12970      * container The id or element that will contain the toolbar
12971      */
12972     /**
12973      * @cfg {Boolean} displayInfo
12974      * True to display the displayMsg (defaults to false)
12975      */
12976     /**
12977      * @cfg {Number} pageSize
12978      * The number of records to display per page (defaults to 20)
12979      */
12980     pageSize: 20,
12981     /**
12982      * @cfg {String} displayMsg
12983      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12984      */
12985     displayMsg : 'Displaying {0} - {1} of {2}',
12986     /**
12987      * @cfg {String} emptyMsg
12988      * The message to display when no records are found (defaults to "No data to display")
12989      */
12990     emptyMsg : 'No data to display',
12991     /**
12992      * Customizable piece of the default paging text (defaults to "Page")
12993      * @type String
12994      */
12995     beforePageText : "Page",
12996     /**
12997      * Customizable piece of the default paging text (defaults to "of %0")
12998      * @type String
12999      */
13000     afterPageText : "of {0}",
13001     /**
13002      * Customizable piece of the default paging text (defaults to "First Page")
13003      * @type String
13004      */
13005     firstText : "First Page",
13006     /**
13007      * Customizable piece of the default paging text (defaults to "Previous Page")
13008      * @type String
13009      */
13010     prevText : "Previous Page",
13011     /**
13012      * Customizable piece of the default paging text (defaults to "Next Page")
13013      * @type String
13014      */
13015     nextText : "Next Page",
13016     /**
13017      * Customizable piece of the default paging text (defaults to "Last Page")
13018      * @type String
13019      */
13020     lastText : "Last Page",
13021     /**
13022      * Customizable piece of the default paging text (defaults to "Refresh")
13023      * @type String
13024      */
13025     refreshText : "Refresh",
13026
13027     // private
13028     renderButtons : function(el){
13029         Roo.PagingToolbar.superclass.render.call(this, el);
13030         this.first = this.addButton({
13031             tooltip: this.firstText,
13032             cls: "x-btn-icon x-grid-page-first",
13033             disabled: true,
13034             handler: this.onClick.createDelegate(this, ["first"])
13035         });
13036         this.prev = this.addButton({
13037             tooltip: this.prevText,
13038             cls: "x-btn-icon x-grid-page-prev",
13039             disabled: true,
13040             handler: this.onClick.createDelegate(this, ["prev"])
13041         });
13042         //this.addSeparator();
13043         this.add(this.beforePageText);
13044         this.field = Roo.get(this.addDom({
13045            tag: "input",
13046            type: "text",
13047            size: "3",
13048            value: "1",
13049            cls: "x-grid-page-number"
13050         }).el);
13051         this.field.on("keydown", this.onPagingKeydown, this);
13052         this.field.on("focus", function(){this.dom.select();});
13053         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
13054         this.field.setHeight(18);
13055         //this.addSeparator();
13056         this.next = this.addButton({
13057             tooltip: this.nextText,
13058             cls: "x-btn-icon x-grid-page-next",
13059             disabled: true,
13060             handler: this.onClick.createDelegate(this, ["next"])
13061         });
13062         this.last = this.addButton({
13063             tooltip: this.lastText,
13064             cls: "x-btn-icon x-grid-page-last",
13065             disabled: true,
13066             handler: this.onClick.createDelegate(this, ["last"])
13067         });
13068         //this.addSeparator();
13069         this.loading = this.addButton({
13070             tooltip: this.refreshText,
13071             cls: "x-btn-icon x-grid-loading",
13072             handler: this.onClick.createDelegate(this, ["refresh"])
13073         });
13074
13075         if(this.displayInfo){
13076             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
13077         }
13078     },
13079
13080     // private
13081     updateInfo : function(){
13082         if(this.displayEl){
13083             var count = this.ds.getCount();
13084             var msg = count == 0 ?
13085                 this.emptyMsg :
13086                 String.format(
13087                     this.displayMsg,
13088                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
13089                 );
13090             this.displayEl.update(msg);
13091         }
13092     },
13093
13094     // private
13095     onLoad : function(ds, r, o){
13096        this.cursor = o.params ? o.params.start : 0;
13097        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
13098
13099        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
13100        this.field.dom.value = ap;
13101        this.first.setDisabled(ap == 1);
13102        this.prev.setDisabled(ap == 1);
13103        this.next.setDisabled(ap == ps);
13104        this.last.setDisabled(ap == ps);
13105        this.loading.enable();
13106        this.updateInfo();
13107     },
13108
13109     // private
13110     getPageData : function(){
13111         var total = this.ds.getTotalCount();
13112         return {
13113             total : total,
13114             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
13115             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
13116         };
13117     },
13118
13119     // private
13120     onLoadError : function(){
13121         this.loading.enable();
13122     },
13123
13124     // private
13125     onPagingKeydown : function(e){
13126         var k = e.getKey();
13127         var d = this.getPageData();
13128         if(k == e.RETURN){
13129             var v = this.field.dom.value, pageNum;
13130             if(!v || isNaN(pageNum = parseInt(v, 10))){
13131                 this.field.dom.value = d.activePage;
13132                 return;
13133             }
13134             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
13135             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13136             e.stopEvent();
13137         }
13138         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))
13139         {
13140           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
13141           this.field.dom.value = pageNum;
13142           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13143           e.stopEvent();
13144         }
13145         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13146         {
13147           var v = this.field.dom.value, pageNum; 
13148           var increment = (e.shiftKey) ? 10 : 1;
13149           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13150             increment *= -1;
13151           if(!v || isNaN(pageNum = parseInt(v, 10))) {
13152             this.field.dom.value = d.activePage;
13153             return;
13154           }
13155           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13156           {
13157             this.field.dom.value = parseInt(v, 10) + increment;
13158             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13159             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13160           }
13161           e.stopEvent();
13162         }
13163     },
13164
13165     // private
13166     beforeLoad : function(){
13167         if(this.loading){
13168             this.loading.disable();
13169         }
13170     },
13171
13172     // private
13173     onClick : function(which){
13174         var ds = this.ds;
13175         switch(which){
13176             case "first":
13177                 ds.load({params:{start: 0, limit: this.pageSize}});
13178             break;
13179             case "prev":
13180                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13181             break;
13182             case "next":
13183                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13184             break;
13185             case "last":
13186                 var total = ds.getTotalCount();
13187                 var extra = total % this.pageSize;
13188                 var lastStart = extra ? (total - extra) : total-this.pageSize;
13189                 ds.load({params:{start: lastStart, limit: this.pageSize}});
13190             break;
13191             case "refresh":
13192                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13193             break;
13194         }
13195     },
13196
13197     /**
13198      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13199      * @param {Roo.data.Store} store The data store to unbind
13200      */
13201     unbind : function(ds){
13202         ds.un("beforeload", this.beforeLoad, this);
13203         ds.un("load", this.onLoad, this);
13204         ds.un("loadexception", this.onLoadError, this);
13205         ds.un("remove", this.updateInfo, this);
13206         ds.un("add", this.updateInfo, this);
13207         this.ds = undefined;
13208     },
13209
13210     /**
13211      * Binds the paging toolbar to the specified {@link Roo.data.Store}
13212      * @param {Roo.data.Store} store The data store to bind
13213      */
13214     bind : function(ds){
13215         ds.on("beforeload", this.beforeLoad, this);
13216         ds.on("load", this.onLoad, this);
13217         ds.on("loadexception", this.onLoadError, this);
13218         ds.on("remove", this.updateInfo, this);
13219         ds.on("add", this.updateInfo, this);
13220         this.ds = ds;
13221     }
13222 });/*
13223  * Based on:
13224  * Ext JS Library 1.1.1
13225  * Copyright(c) 2006-2007, Ext JS, LLC.
13226  *
13227  * Originally Released Under LGPL - original licence link has changed is not relivant.
13228  *
13229  * Fork - LGPL
13230  * <script type="text/javascript">
13231  */
13232
13233 /**
13234  * @class Roo.Resizable
13235  * @extends Roo.util.Observable
13236  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13237  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13238  * 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
13239  * the element will be wrapped for you automatically.</p>
13240  * <p>Here is the list of valid resize handles:</p>
13241  * <pre>
13242 Value   Description
13243 ------  -------------------
13244  'n'     north
13245  's'     south
13246  'e'     east
13247  'w'     west
13248  'nw'    northwest
13249  'sw'    southwest
13250  'se'    southeast
13251  'ne'    northeast
13252  'hd'    horizontal drag
13253  'all'   all
13254 </pre>
13255  * <p>Here's an example showing the creation of a typical Resizable:</p>
13256  * <pre><code>
13257 var resizer = new Roo.Resizable("element-id", {
13258     handles: 'all',
13259     minWidth: 200,
13260     minHeight: 100,
13261     maxWidth: 500,
13262     maxHeight: 400,
13263     pinned: true
13264 });
13265 resizer.on("resize", myHandler);
13266 </code></pre>
13267  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13268  * resizer.east.setDisplayed(false);</p>
13269  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13270  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13271  * resize operation's new size (defaults to [0, 0])
13272  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13273  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13274  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13275  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13276  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13277  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13278  * @cfg {Number} width The width of the element in pixels (defaults to null)
13279  * @cfg {Number} height The height of the element in pixels (defaults to null)
13280  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13281  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13282  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13283  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13284  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13285  * in favor of the handles config option (defaults to false)
13286  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13287  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13288  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13289  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13290  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13291  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13292  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13293  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13294  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13295  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13296  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13297  * @constructor
13298  * Create a new resizable component
13299  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13300  * @param {Object} config configuration options
13301   */
13302 Roo.Resizable = function(el, config)
13303 {
13304     this.el = Roo.get(el);
13305
13306     if(config && config.wrap){
13307         config.resizeChild = this.el;
13308         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13309         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13310         this.el.setStyle("overflow", "hidden");
13311         this.el.setPositioning(config.resizeChild.getPositioning());
13312         config.resizeChild.clearPositioning();
13313         if(!config.width || !config.height){
13314             var csize = config.resizeChild.getSize();
13315             this.el.setSize(csize.width, csize.height);
13316         }
13317         if(config.pinned && !config.adjustments){
13318             config.adjustments = "auto";
13319         }
13320     }
13321
13322     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13323     this.proxy.unselectable();
13324     this.proxy.enableDisplayMode('block');
13325
13326     Roo.apply(this, config);
13327
13328     if(this.pinned){
13329         this.disableTrackOver = true;
13330         this.el.addClass("x-resizable-pinned");
13331     }
13332     // if the element isn't positioned, make it relative
13333     var position = this.el.getStyle("position");
13334     if(position != "absolute" && position != "fixed"){
13335         this.el.setStyle("position", "relative");
13336     }
13337     if(!this.handles){ // no handles passed, must be legacy style
13338         this.handles = 's,e,se';
13339         if(this.multiDirectional){
13340             this.handles += ',n,w';
13341         }
13342     }
13343     if(this.handles == "all"){
13344         this.handles = "n s e w ne nw se sw";
13345     }
13346     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13347     var ps = Roo.Resizable.positions;
13348     for(var i = 0, len = hs.length; i < len; i++){
13349         if(hs[i] && ps[hs[i]]){
13350             var pos = ps[hs[i]];
13351             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13352         }
13353     }
13354     // legacy
13355     this.corner = this.southeast;
13356     
13357     // updateBox = the box can move..
13358     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13359         this.updateBox = true;
13360     }
13361
13362     this.activeHandle = null;
13363
13364     if(this.resizeChild){
13365         if(typeof this.resizeChild == "boolean"){
13366             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13367         }else{
13368             this.resizeChild = Roo.get(this.resizeChild, true);
13369         }
13370     }
13371     
13372     if(this.adjustments == "auto"){
13373         var rc = this.resizeChild;
13374         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13375         if(rc && (hw || hn)){
13376             rc.position("relative");
13377             rc.setLeft(hw ? hw.el.getWidth() : 0);
13378             rc.setTop(hn ? hn.el.getHeight() : 0);
13379         }
13380         this.adjustments = [
13381             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13382             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13383         ];
13384     }
13385
13386     if(this.draggable){
13387         this.dd = this.dynamic ?
13388             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13389         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13390     }
13391
13392     // public events
13393     this.addEvents({
13394         /**
13395          * @event beforeresize
13396          * Fired before resize is allowed. Set enabled to false to cancel resize.
13397          * @param {Roo.Resizable} this
13398          * @param {Roo.EventObject} e The mousedown event
13399          */
13400         "beforeresize" : true,
13401         /**
13402          * @event resize
13403          * Fired after a resize.
13404          * @param {Roo.Resizable} this
13405          * @param {Number} width The new width
13406          * @param {Number} height The new height
13407          * @param {Roo.EventObject} e The mouseup event
13408          */
13409         "resize" : true
13410     });
13411
13412     if(this.width !== null && this.height !== null){
13413         this.resizeTo(this.width, this.height);
13414     }else{
13415         this.updateChildSize();
13416     }
13417     if(Roo.isIE){
13418         this.el.dom.style.zoom = 1;
13419     }
13420     Roo.Resizable.superclass.constructor.call(this);
13421 };
13422
13423 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13424         resizeChild : false,
13425         adjustments : [0, 0],
13426         minWidth : 5,
13427         minHeight : 5,
13428         maxWidth : 10000,
13429         maxHeight : 10000,
13430         enabled : true,
13431         animate : false,
13432         duration : .35,
13433         dynamic : false,
13434         handles : false,
13435         multiDirectional : false,
13436         disableTrackOver : false,
13437         easing : 'easeOutStrong',
13438         widthIncrement : 0,
13439         heightIncrement : 0,
13440         pinned : false,
13441         width : null,
13442         height : null,
13443         preserveRatio : false,
13444         transparent: false,
13445         minX: 0,
13446         minY: 0,
13447         draggable: false,
13448
13449         /**
13450          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13451          */
13452         constrainTo: undefined,
13453         /**
13454          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13455          */
13456         resizeRegion: undefined,
13457
13458
13459     /**
13460      * Perform a manual resize
13461      * @param {Number} width
13462      * @param {Number} height
13463      */
13464     resizeTo : function(width, height){
13465         this.el.setSize(width, height);
13466         this.updateChildSize();
13467         this.fireEvent("resize", this, width, height, null);
13468     },
13469
13470     // private
13471     startSizing : function(e, handle){
13472         this.fireEvent("beforeresize", this, e);
13473         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13474
13475             if(!this.overlay){
13476                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13477                 this.overlay.unselectable();
13478                 this.overlay.enableDisplayMode("block");
13479                 this.overlay.on("mousemove", this.onMouseMove, this);
13480                 this.overlay.on("mouseup", this.onMouseUp, this);
13481             }
13482             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13483
13484             this.resizing = true;
13485             this.startBox = this.el.getBox();
13486             this.startPoint = e.getXY();
13487             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13488                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13489
13490             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13491             this.overlay.show();
13492
13493             if(this.constrainTo) {
13494                 var ct = Roo.get(this.constrainTo);
13495                 this.resizeRegion = ct.getRegion().adjust(
13496                     ct.getFrameWidth('t'),
13497                     ct.getFrameWidth('l'),
13498                     -ct.getFrameWidth('b'),
13499                     -ct.getFrameWidth('r')
13500                 );
13501             }
13502
13503             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13504             this.proxy.show();
13505             this.proxy.setBox(this.startBox);
13506             if(!this.dynamic){
13507                 this.proxy.setStyle('visibility', 'visible');
13508             }
13509         }
13510     },
13511
13512     // private
13513     onMouseDown : function(handle, e){
13514         if(this.enabled){
13515             e.stopEvent();
13516             this.activeHandle = handle;
13517             this.startSizing(e, handle);
13518         }
13519     },
13520
13521     // private
13522     onMouseUp : function(e){
13523         var size = this.resizeElement();
13524         this.resizing = false;
13525         this.handleOut();
13526         this.overlay.hide();
13527         this.proxy.hide();
13528         this.fireEvent("resize", this, size.width, size.height, e);
13529     },
13530
13531     // private
13532     updateChildSize : function(){
13533         if(this.resizeChild){
13534             var el = this.el;
13535             var child = this.resizeChild;
13536             var adj = this.adjustments;
13537             if(el.dom.offsetWidth){
13538                 var b = el.getSize(true);
13539                 child.setSize(b.width+adj[0], b.height+adj[1]);
13540             }
13541             // Second call here for IE
13542             // The first call enables instant resizing and
13543             // the second call corrects scroll bars if they
13544             // exist
13545             if(Roo.isIE){
13546                 setTimeout(function(){
13547                     if(el.dom.offsetWidth){
13548                         var b = el.getSize(true);
13549                         child.setSize(b.width+adj[0], b.height+adj[1]);
13550                     }
13551                 }, 10);
13552             }
13553         }
13554     },
13555
13556     // private
13557     snap : function(value, inc, min){
13558         if(!inc || !value) return value;
13559         var newValue = value;
13560         var m = value % inc;
13561         if(m > 0){
13562             if(m > (inc/2)){
13563                 newValue = value + (inc-m);
13564             }else{
13565                 newValue = value - m;
13566             }
13567         }
13568         return Math.max(min, newValue);
13569     },
13570
13571     // private
13572     resizeElement : function(){
13573         var box = this.proxy.getBox();
13574         if(this.updateBox){
13575             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13576         }else{
13577             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13578         }
13579         this.updateChildSize();
13580         if(!this.dynamic){
13581             this.proxy.hide();
13582         }
13583         return box;
13584     },
13585
13586     // private
13587     constrain : function(v, diff, m, mx){
13588         if(v - diff < m){
13589             diff = v - m;
13590         }else if(v - diff > mx){
13591             diff = mx - v;
13592         }
13593         return diff;
13594     },
13595
13596     // private
13597     onMouseMove : function(e){
13598         if(this.enabled){
13599             try{// try catch so if something goes wrong the user doesn't get hung
13600
13601             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13602                 return;
13603             }
13604
13605             //var curXY = this.startPoint;
13606             var curSize = this.curSize || this.startBox;
13607             var x = this.startBox.x, y = this.startBox.y;
13608             var ox = x, oy = y;
13609             var w = curSize.width, h = curSize.height;
13610             var ow = w, oh = h;
13611             var mw = this.minWidth, mh = this.minHeight;
13612             var mxw = this.maxWidth, mxh = this.maxHeight;
13613             var wi = this.widthIncrement;
13614             var hi = this.heightIncrement;
13615
13616             var eventXY = e.getXY();
13617             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13618             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13619
13620             var pos = this.activeHandle.position;
13621
13622             switch(pos){
13623                 case "east":
13624                     w += diffX;
13625                     w = Math.min(Math.max(mw, w), mxw);
13626                     break;
13627              
13628                 case "south":
13629                     h += diffY;
13630                     h = Math.min(Math.max(mh, h), mxh);
13631                     break;
13632                 case "southeast":
13633                     w += diffX;
13634                     h += diffY;
13635                     w = Math.min(Math.max(mw, w), mxw);
13636                     h = Math.min(Math.max(mh, h), mxh);
13637                     break;
13638                 case "north":
13639                     diffY = this.constrain(h, diffY, mh, mxh);
13640                     y += diffY;
13641                     h -= diffY;
13642                     break;
13643                 case "hdrag":
13644                     
13645                     if (wi) {
13646                         var adiffX = Math.abs(diffX);
13647                         var sub = (adiffX % wi); // how much 
13648                         if (sub > (wi/2)) { // far enough to snap
13649                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13650                         } else {
13651                             // remove difference.. 
13652                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13653                         }
13654                     }
13655                     x += diffX;
13656                     x = Math.max(this.minX, x);
13657                     break;
13658                 case "west":
13659                     diffX = this.constrain(w, diffX, mw, mxw);
13660                     x += diffX;
13661                     w -= diffX;
13662                     break;
13663                 case "northeast":
13664                     w += diffX;
13665                     w = Math.min(Math.max(mw, w), mxw);
13666                     diffY = this.constrain(h, diffY, mh, mxh);
13667                     y += diffY;
13668                     h -= diffY;
13669                     break;
13670                 case "northwest":
13671                     diffX = this.constrain(w, diffX, mw, mxw);
13672                     diffY = this.constrain(h, diffY, mh, mxh);
13673                     y += diffY;
13674                     h -= diffY;
13675                     x += diffX;
13676                     w -= diffX;
13677                     break;
13678                case "southwest":
13679                     diffX = this.constrain(w, diffX, mw, mxw);
13680                     h += diffY;
13681                     h = Math.min(Math.max(mh, h), mxh);
13682                     x += diffX;
13683                     w -= diffX;
13684                     break;
13685             }
13686
13687             var sw = this.snap(w, wi, mw);
13688             var sh = this.snap(h, hi, mh);
13689             if(sw != w || sh != h){
13690                 switch(pos){
13691                     case "northeast":
13692                         y -= sh - h;
13693                     break;
13694                     case "north":
13695                         y -= sh - h;
13696                         break;
13697                     case "southwest":
13698                         x -= sw - w;
13699                     break;
13700                     case "west":
13701                         x -= sw - w;
13702                         break;
13703                     case "northwest":
13704                         x -= sw - w;
13705                         y -= sh - h;
13706                     break;
13707                 }
13708                 w = sw;
13709                 h = sh;
13710             }
13711
13712             if(this.preserveRatio){
13713                 switch(pos){
13714                     case "southeast":
13715                     case "east":
13716                         h = oh * (w/ow);
13717                         h = Math.min(Math.max(mh, h), mxh);
13718                         w = ow * (h/oh);
13719                        break;
13720                     case "south":
13721                         w = ow * (h/oh);
13722                         w = Math.min(Math.max(mw, w), mxw);
13723                         h = oh * (w/ow);
13724                         break;
13725                     case "northeast":
13726                         w = ow * (h/oh);
13727                         w = Math.min(Math.max(mw, w), mxw);
13728                         h = oh * (w/ow);
13729                     break;
13730                     case "north":
13731                         var tw = w;
13732                         w = ow * (h/oh);
13733                         w = Math.min(Math.max(mw, w), mxw);
13734                         h = oh * (w/ow);
13735                         x += (tw - w) / 2;
13736                         break;
13737                     case "southwest":
13738                         h = oh * (w/ow);
13739                         h = Math.min(Math.max(mh, h), mxh);
13740                         var tw = w;
13741                         w = ow * (h/oh);
13742                         x += tw - w;
13743                         break;
13744                     case "west":
13745                         var th = h;
13746                         h = oh * (w/ow);
13747                         h = Math.min(Math.max(mh, h), mxh);
13748                         y += (th - h) / 2;
13749                         var tw = w;
13750                         w = ow * (h/oh);
13751                         x += tw - w;
13752                        break;
13753                     case "northwest":
13754                         var tw = w;
13755                         var th = h;
13756                         h = oh * (w/ow);
13757                         h = Math.min(Math.max(mh, h), mxh);
13758                         w = ow * (h/oh);
13759                         y += th - h;
13760                         x += tw - w;
13761                        break;
13762
13763                 }
13764             }
13765             if (pos == 'hdrag') {
13766                 w = ow;
13767             }
13768             this.proxy.setBounds(x, y, w, h);
13769             if(this.dynamic){
13770                 this.resizeElement();
13771             }
13772             }catch(e){}
13773         }
13774     },
13775
13776     // private
13777     handleOver : function(){
13778         if(this.enabled){
13779             this.el.addClass("x-resizable-over");
13780         }
13781     },
13782
13783     // private
13784     handleOut : function(){
13785         if(!this.resizing){
13786             this.el.removeClass("x-resizable-over");
13787         }
13788     },
13789
13790     /**
13791      * Returns the element this component is bound to.
13792      * @return {Roo.Element}
13793      */
13794     getEl : function(){
13795         return this.el;
13796     },
13797
13798     /**
13799      * Returns the resizeChild element (or null).
13800      * @return {Roo.Element}
13801      */
13802     getResizeChild : function(){
13803         return this.resizeChild;
13804     },
13805
13806     /**
13807      * Destroys this resizable. If the element was wrapped and
13808      * removeEl is not true then the element remains.
13809      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13810      */
13811     destroy : function(removeEl){
13812         this.proxy.remove();
13813         if(this.overlay){
13814             this.overlay.removeAllListeners();
13815             this.overlay.remove();
13816         }
13817         var ps = Roo.Resizable.positions;
13818         for(var k in ps){
13819             if(typeof ps[k] != "function" && this[ps[k]]){
13820                 var h = this[ps[k]];
13821                 h.el.removeAllListeners();
13822                 h.el.remove();
13823             }
13824         }
13825         if(removeEl){
13826             this.el.update("");
13827             this.el.remove();
13828         }
13829     }
13830 });
13831
13832 // private
13833 // hash to map config positions to true positions
13834 Roo.Resizable.positions = {
13835     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13836     hd: "hdrag"
13837 };
13838
13839 // private
13840 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13841     if(!this.tpl){
13842         // only initialize the template if resizable is used
13843         var tpl = Roo.DomHelper.createTemplate(
13844             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13845         );
13846         tpl.compile();
13847         Roo.Resizable.Handle.prototype.tpl = tpl;
13848     }
13849     this.position = pos;
13850     this.rz = rz;
13851     // show north drag fro topdra
13852     var handlepos = pos == 'hdrag' ? 'north' : pos;
13853     
13854     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13855     if (pos == 'hdrag') {
13856         this.el.setStyle('cursor', 'pointer');
13857     }
13858     this.el.unselectable();
13859     if(transparent){
13860         this.el.setOpacity(0);
13861     }
13862     this.el.on("mousedown", this.onMouseDown, this);
13863     if(!disableTrackOver){
13864         this.el.on("mouseover", this.onMouseOver, this);
13865         this.el.on("mouseout", this.onMouseOut, this);
13866     }
13867 };
13868
13869 // private
13870 Roo.Resizable.Handle.prototype = {
13871     afterResize : function(rz){
13872         // do nothing
13873     },
13874     // private
13875     onMouseDown : function(e){
13876         this.rz.onMouseDown(this, e);
13877     },
13878     // private
13879     onMouseOver : function(e){
13880         this.rz.handleOver(this, e);
13881     },
13882     // private
13883     onMouseOut : function(e){
13884         this.rz.handleOut(this, e);
13885     }
13886 };/*
13887  * Based on:
13888  * Ext JS Library 1.1.1
13889  * Copyright(c) 2006-2007, Ext JS, LLC.
13890  *
13891  * Originally Released Under LGPL - original licence link has changed is not relivant.
13892  *
13893  * Fork - LGPL
13894  * <script type="text/javascript">
13895  */
13896
13897 /**
13898  * @class Roo.Editor
13899  * @extends Roo.Component
13900  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13901  * @constructor
13902  * Create a new Editor
13903  * @param {Roo.form.Field} field The Field object (or descendant)
13904  * @param {Object} config The config object
13905  */
13906 Roo.Editor = function(field, config){
13907     Roo.Editor.superclass.constructor.call(this, config);
13908     this.field = field;
13909     this.addEvents({
13910         /**
13911              * @event beforestartedit
13912              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13913              * false from the handler of this event.
13914              * @param {Editor} this
13915              * @param {Roo.Element} boundEl The underlying element bound to this editor
13916              * @param {Mixed} value The field value being set
13917              */
13918         "beforestartedit" : true,
13919         /**
13920              * @event startedit
13921              * Fires when this editor is displayed
13922              * @param {Roo.Element} boundEl The underlying element bound to this editor
13923              * @param {Mixed} value The starting field value
13924              */
13925         "startedit" : true,
13926         /**
13927              * @event beforecomplete
13928              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13929              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13930              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13931              * event will not fire since no edit actually occurred.
13932              * @param {Editor} this
13933              * @param {Mixed} value The current field value
13934              * @param {Mixed} startValue The original field value
13935              */
13936         "beforecomplete" : true,
13937         /**
13938              * @event complete
13939              * Fires after editing is complete and any changed value has been written to the underlying field.
13940              * @param {Editor} this
13941              * @param {Mixed} value The current field value
13942              * @param {Mixed} startValue The original field value
13943              */
13944         "complete" : true,
13945         /**
13946          * @event specialkey
13947          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13948          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13949          * @param {Roo.form.Field} this
13950          * @param {Roo.EventObject} e The event object
13951          */
13952         "specialkey" : true
13953     });
13954 };
13955
13956 Roo.extend(Roo.Editor, Roo.Component, {
13957     /**
13958      * @cfg {Boolean/String} autosize
13959      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13960      * or "height" to adopt the height only (defaults to false)
13961      */
13962     /**
13963      * @cfg {Boolean} revertInvalid
13964      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13965      * validation fails (defaults to true)
13966      */
13967     /**
13968      * @cfg {Boolean} ignoreNoChange
13969      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13970      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
13971      * will never be ignored.
13972      */
13973     /**
13974      * @cfg {Boolean} hideEl
13975      * False to keep the bound element visible while the editor is displayed (defaults to true)
13976      */
13977     /**
13978      * @cfg {Mixed} value
13979      * The data value of the underlying field (defaults to "")
13980      */
13981     value : "",
13982     /**
13983      * @cfg {String} alignment
13984      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13985      */
13986     alignment: "c-c?",
13987     /**
13988      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13989      * for bottom-right shadow (defaults to "frame")
13990      */
13991     shadow : "frame",
13992     /**
13993      * @cfg {Boolean} constrain True to constrain the editor to the viewport
13994      */
13995     constrain : false,
13996     /**
13997      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13998      */
13999     completeOnEnter : false,
14000     /**
14001      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
14002      */
14003     cancelOnEsc : false,
14004     /**
14005      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
14006      */
14007     updateEl : false,
14008
14009     // private
14010     onRender : function(ct, position){
14011         this.el = new Roo.Layer({
14012             shadow: this.shadow,
14013             cls: "x-editor",
14014             parentEl : ct,
14015             shim : this.shim,
14016             shadowOffset:4,
14017             id: this.id,
14018             constrain: this.constrain
14019         });
14020         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
14021         if(this.field.msgTarget != 'title'){
14022             this.field.msgTarget = 'qtip';
14023         }
14024         this.field.render(this.el);
14025         if(Roo.isGecko){
14026             this.field.el.dom.setAttribute('autocomplete', 'off');
14027         }
14028         this.field.on("specialkey", this.onSpecialKey, this);
14029         if(this.swallowKeys){
14030             this.field.el.swallowEvent(['keydown','keypress']);
14031         }
14032         this.field.show();
14033         this.field.on("blur", this.onBlur, this);
14034         if(this.field.grow){
14035             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
14036         }
14037     },
14038
14039     onSpecialKey : function(field, e)
14040     {
14041         //Roo.log('editor onSpecialKey');
14042         if(this.completeOnEnter && e.getKey() == e.ENTER){
14043             e.stopEvent();
14044             this.completeEdit();
14045             return;
14046         }
14047         // do not fire special key otherwise it might hide close the editor...
14048         if(e.getKey() == e.ENTER){    
14049             return;
14050         }
14051         if(this.cancelOnEsc && e.getKey() == e.ESC){
14052             this.cancelEdit();
14053             return;
14054         } 
14055         this.fireEvent('specialkey', field, e);
14056     
14057     },
14058
14059     /**
14060      * Starts the editing process and shows the editor.
14061      * @param {String/HTMLElement/Element} el The element to edit
14062      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
14063       * to the innerHTML of el.
14064      */
14065     startEdit : function(el, value){
14066         if(this.editing){
14067             this.completeEdit();
14068         }
14069         this.boundEl = Roo.get(el);
14070         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
14071         if(!this.rendered){
14072             this.render(this.parentEl || document.body);
14073         }
14074         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
14075             return;
14076         }
14077         this.startValue = v;
14078         this.field.setValue(v);
14079         if(this.autoSize){
14080             var sz = this.boundEl.getSize();
14081             switch(this.autoSize){
14082                 case "width":
14083                 this.setSize(sz.width,  "");
14084                 break;
14085                 case "height":
14086                 this.setSize("",  sz.height);
14087                 break;
14088                 default:
14089                 this.setSize(sz.width,  sz.height);
14090             }
14091         }
14092         this.el.alignTo(this.boundEl, this.alignment);
14093         this.editing = true;
14094         if(Roo.QuickTips){
14095             Roo.QuickTips.disable();
14096         }
14097         this.show();
14098     },
14099
14100     /**
14101      * Sets the height and width of this editor.
14102      * @param {Number} width The new width
14103      * @param {Number} height The new height
14104      */
14105     setSize : function(w, h){
14106         this.field.setSize(w, h);
14107         if(this.el){
14108             this.el.sync();
14109         }
14110     },
14111
14112     /**
14113      * Realigns the editor to the bound field based on the current alignment config value.
14114      */
14115     realign : function(){
14116         this.el.alignTo(this.boundEl, this.alignment);
14117     },
14118
14119     /**
14120      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
14121      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
14122      */
14123     completeEdit : function(remainVisible){
14124         if(!this.editing){
14125             return;
14126         }
14127         var v = this.getValue();
14128         if(this.revertInvalid !== false && !this.field.isValid()){
14129             v = this.startValue;
14130             this.cancelEdit(true);
14131         }
14132         if(String(v) === String(this.startValue) && this.ignoreNoChange){
14133             this.editing = false;
14134             this.hide();
14135             return;
14136         }
14137         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
14138             this.editing = false;
14139             if(this.updateEl && this.boundEl){
14140                 this.boundEl.update(v);
14141             }
14142             if(remainVisible !== true){
14143                 this.hide();
14144             }
14145             this.fireEvent("complete", this, v, this.startValue);
14146         }
14147     },
14148
14149     // private
14150     onShow : function(){
14151         this.el.show();
14152         if(this.hideEl !== false){
14153             this.boundEl.hide();
14154         }
14155         this.field.show();
14156         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14157             this.fixIEFocus = true;
14158             this.deferredFocus.defer(50, this);
14159         }else{
14160             this.field.focus();
14161         }
14162         this.fireEvent("startedit", this.boundEl, this.startValue);
14163     },
14164
14165     deferredFocus : function(){
14166         if(this.editing){
14167             this.field.focus();
14168         }
14169     },
14170
14171     /**
14172      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
14173      * reverted to the original starting value.
14174      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14175      * cancel (defaults to false)
14176      */
14177     cancelEdit : function(remainVisible){
14178         if(this.editing){
14179             this.setValue(this.startValue);
14180             if(remainVisible !== true){
14181                 this.hide();
14182             }
14183         }
14184     },
14185
14186     // private
14187     onBlur : function(){
14188         if(this.allowBlur !== true && this.editing){
14189             this.completeEdit();
14190         }
14191     },
14192
14193     // private
14194     onHide : function(){
14195         if(this.editing){
14196             this.completeEdit();
14197             return;
14198         }
14199         this.field.blur();
14200         if(this.field.collapse){
14201             this.field.collapse();
14202         }
14203         this.el.hide();
14204         if(this.hideEl !== false){
14205             this.boundEl.show();
14206         }
14207         if(Roo.QuickTips){
14208             Roo.QuickTips.enable();
14209         }
14210     },
14211
14212     /**
14213      * Sets the data value of the editor
14214      * @param {Mixed} value Any valid value supported by the underlying field
14215      */
14216     setValue : function(v){
14217         this.field.setValue(v);
14218     },
14219
14220     /**
14221      * Gets the data value of the editor
14222      * @return {Mixed} The data value
14223      */
14224     getValue : function(){
14225         return this.field.getValue();
14226     }
14227 });/*
14228  * Based on:
14229  * Ext JS Library 1.1.1
14230  * Copyright(c) 2006-2007, Ext JS, LLC.
14231  *
14232  * Originally Released Under LGPL - original licence link has changed is not relivant.
14233  *
14234  * Fork - LGPL
14235  * <script type="text/javascript">
14236  */
14237  
14238 /**
14239  * @class Roo.BasicDialog
14240  * @extends Roo.util.Observable
14241  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
14242  * <pre><code>
14243 var dlg = new Roo.BasicDialog("my-dlg", {
14244     height: 200,
14245     width: 300,
14246     minHeight: 100,
14247     minWidth: 150,
14248     modal: true,
14249     proxyDrag: true,
14250     shadow: true
14251 });
14252 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14253 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14254 dlg.addButton('Cancel', dlg.hide, dlg);
14255 dlg.show();
14256 </code></pre>
14257   <b>A Dialog should always be a direct child of the body element.</b>
14258  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14259  * @cfg {String} title Default text to display in the title bar (defaults to null)
14260  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14261  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14262  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14263  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14264  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14265  * (defaults to null with no animation)
14266  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14267  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14268  * property for valid values (defaults to 'all')
14269  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14270  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14271  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14272  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14273  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14274  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14275  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14276  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14277  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14278  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14279  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14280  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14281  * draggable = true (defaults to false)
14282  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14283  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14284  * shadow (defaults to false)
14285  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14286  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14287  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14288  * @cfg {Array} buttons Array of buttons
14289  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14290  * @constructor
14291  * Create a new BasicDialog.
14292  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14293  * @param {Object} config Configuration options
14294  */
14295 Roo.BasicDialog = function(el, config){
14296     this.el = Roo.get(el);
14297     var dh = Roo.DomHelper;
14298     if(!this.el && config && config.autoCreate){
14299         if(typeof config.autoCreate == "object"){
14300             if(!config.autoCreate.id){
14301                 config.autoCreate.id = el;
14302             }
14303             this.el = dh.append(document.body,
14304                         config.autoCreate, true);
14305         }else{
14306             this.el = dh.append(document.body,
14307                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14308         }
14309     }
14310     el = this.el;
14311     el.setDisplayed(true);
14312     el.hide = this.hideAction;
14313     this.id = el.id;
14314     el.addClass("x-dlg");
14315
14316     Roo.apply(this, config);
14317
14318     this.proxy = el.createProxy("x-dlg-proxy");
14319     this.proxy.hide = this.hideAction;
14320     this.proxy.setOpacity(.5);
14321     this.proxy.hide();
14322
14323     if(config.width){
14324         el.setWidth(config.width);
14325     }
14326     if(config.height){
14327         el.setHeight(config.height);
14328     }
14329     this.size = el.getSize();
14330     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14331         this.xy = [config.x,config.y];
14332     }else{
14333         this.xy = el.getCenterXY(true);
14334     }
14335     /** The header element @type Roo.Element */
14336     this.header = el.child("> .x-dlg-hd");
14337     /** The body element @type Roo.Element */
14338     this.body = el.child("> .x-dlg-bd");
14339     /** The footer element @type Roo.Element */
14340     this.footer = el.child("> .x-dlg-ft");
14341
14342     if(!this.header){
14343         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14344     }
14345     if(!this.body){
14346         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14347     }
14348
14349     this.header.unselectable();
14350     if(this.title){
14351         this.header.update(this.title);
14352     }
14353     // this element allows the dialog to be focused for keyboard event
14354     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14355     this.focusEl.swallowEvent("click", true);
14356
14357     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14358
14359     // wrap the body and footer for special rendering
14360     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14361     if(this.footer){
14362         this.bwrap.dom.appendChild(this.footer.dom);
14363     }
14364
14365     this.bg = this.el.createChild({
14366         tag: "div", cls:"x-dlg-bg",
14367         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14368     });
14369     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14370
14371
14372     if(this.autoScroll !== false && !this.autoTabs){
14373         this.body.setStyle("overflow", "auto");
14374     }
14375
14376     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14377
14378     if(this.closable !== false){
14379         this.el.addClass("x-dlg-closable");
14380         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14381         this.close.on("click", this.closeClick, this);
14382         this.close.addClassOnOver("x-dlg-close-over");
14383     }
14384     if(this.collapsible !== false){
14385         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14386         this.collapseBtn.on("click", this.collapseClick, this);
14387         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14388         this.header.on("dblclick", this.collapseClick, this);
14389     }
14390     if(this.resizable !== false){
14391         this.el.addClass("x-dlg-resizable");
14392         this.resizer = new Roo.Resizable(el, {
14393             minWidth: this.minWidth || 80,
14394             minHeight:this.minHeight || 80,
14395             handles: this.resizeHandles || "all",
14396             pinned: true
14397         });
14398         this.resizer.on("beforeresize", this.beforeResize, this);
14399         this.resizer.on("resize", this.onResize, this);
14400     }
14401     if(this.draggable !== false){
14402         el.addClass("x-dlg-draggable");
14403         if (!this.proxyDrag) {
14404             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14405         }
14406         else {
14407             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14408         }
14409         dd.setHandleElId(this.header.id);
14410         dd.endDrag = this.endMove.createDelegate(this);
14411         dd.startDrag = this.startMove.createDelegate(this);
14412         dd.onDrag = this.onDrag.createDelegate(this);
14413         dd.scroll = false;
14414         this.dd = dd;
14415     }
14416     if(this.modal){
14417         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14418         this.mask.enableDisplayMode("block");
14419         this.mask.hide();
14420         this.el.addClass("x-dlg-modal");
14421     }
14422     if(this.shadow){
14423         this.shadow = new Roo.Shadow({
14424             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14425             offset : this.shadowOffset
14426         });
14427     }else{
14428         this.shadowOffset = 0;
14429     }
14430     if(Roo.useShims && this.shim !== false){
14431         this.shim = this.el.createShim();
14432         this.shim.hide = this.hideAction;
14433         this.shim.hide();
14434     }else{
14435         this.shim = false;
14436     }
14437     if(this.autoTabs){
14438         this.initTabs();
14439     }
14440     if (this.buttons) { 
14441         var bts= this.buttons;
14442         this.buttons = [];
14443         Roo.each(bts, function(b) {
14444             this.addButton(b);
14445         }, this);
14446     }
14447     
14448     
14449     this.addEvents({
14450         /**
14451          * @event keydown
14452          * Fires when a key is pressed
14453          * @param {Roo.BasicDialog} this
14454          * @param {Roo.EventObject} e
14455          */
14456         "keydown" : true,
14457         /**
14458          * @event move
14459          * Fires when this dialog is moved by the user.
14460          * @param {Roo.BasicDialog} this
14461          * @param {Number} x The new page X
14462          * @param {Number} y The new page Y
14463          */
14464         "move" : true,
14465         /**
14466          * @event resize
14467          * Fires when this dialog is resized by the user.
14468          * @param {Roo.BasicDialog} this
14469          * @param {Number} width The new width
14470          * @param {Number} height The new height
14471          */
14472         "resize" : true,
14473         /**
14474          * @event beforehide
14475          * Fires before this dialog is hidden.
14476          * @param {Roo.BasicDialog} this
14477          */
14478         "beforehide" : true,
14479         /**
14480          * @event hide
14481          * Fires when this dialog is hidden.
14482          * @param {Roo.BasicDialog} this
14483          */
14484         "hide" : true,
14485         /**
14486          * @event beforeshow
14487          * Fires before this dialog is shown.
14488          * @param {Roo.BasicDialog} this
14489          */
14490         "beforeshow" : true,
14491         /**
14492          * @event show
14493          * Fires when this dialog is shown.
14494          * @param {Roo.BasicDialog} this
14495          */
14496         "show" : true
14497     });
14498     el.on("keydown", this.onKeyDown, this);
14499     el.on("mousedown", this.toFront, this);
14500     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14501     this.el.hide();
14502     Roo.DialogManager.register(this);
14503     Roo.BasicDialog.superclass.constructor.call(this);
14504 };
14505
14506 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14507     shadowOffset: Roo.isIE ? 6 : 5,
14508     minHeight: 80,
14509     minWidth: 200,
14510     minButtonWidth: 75,
14511     defaultButton: null,
14512     buttonAlign: "right",
14513     tabTag: 'div',
14514     firstShow: true,
14515
14516     /**
14517      * Sets the dialog title text
14518      * @param {String} text The title text to display
14519      * @return {Roo.BasicDialog} this
14520      */
14521     setTitle : function(text){
14522         this.header.update(text);
14523         return this;
14524     },
14525
14526     // private
14527     closeClick : function(){
14528         this.hide();
14529     },
14530
14531     // private
14532     collapseClick : function(){
14533         this[this.collapsed ? "expand" : "collapse"]();
14534     },
14535
14536     /**
14537      * Collapses the dialog to its minimized state (only the title bar is visible).
14538      * Equivalent to the user clicking the collapse dialog button.
14539      */
14540     collapse : function(){
14541         if(!this.collapsed){
14542             this.collapsed = true;
14543             this.el.addClass("x-dlg-collapsed");
14544             this.restoreHeight = this.el.getHeight();
14545             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14546         }
14547     },
14548
14549     /**
14550      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14551      * clicking the expand dialog button.
14552      */
14553     expand : function(){
14554         if(this.collapsed){
14555             this.collapsed = false;
14556             this.el.removeClass("x-dlg-collapsed");
14557             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14558         }
14559     },
14560
14561     /**
14562      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14563      * @return {Roo.TabPanel} The tabs component
14564      */
14565     initTabs : function(){
14566         var tabs = this.getTabs();
14567         while(tabs.getTab(0)){
14568             tabs.removeTab(0);
14569         }
14570         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14571             var dom = el.dom;
14572             tabs.addTab(Roo.id(dom), dom.title);
14573             dom.title = "";
14574         });
14575         tabs.activate(0);
14576         return tabs;
14577     },
14578
14579     // private
14580     beforeResize : function(){
14581         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14582     },
14583
14584     // private
14585     onResize : function(){
14586         this.refreshSize();
14587         this.syncBodyHeight();
14588         this.adjustAssets();
14589         this.focus();
14590         this.fireEvent("resize", this, this.size.width, this.size.height);
14591     },
14592
14593     // private
14594     onKeyDown : function(e){
14595         if(this.isVisible()){
14596             this.fireEvent("keydown", this, e);
14597         }
14598     },
14599
14600     /**
14601      * Resizes the dialog.
14602      * @param {Number} width
14603      * @param {Number} height
14604      * @return {Roo.BasicDialog} this
14605      */
14606     resizeTo : function(width, height){
14607         this.el.setSize(width, height);
14608         this.size = {width: width, height: height};
14609         this.syncBodyHeight();
14610         if(this.fixedcenter){
14611             this.center();
14612         }
14613         if(this.isVisible()){
14614             this.constrainXY();
14615             this.adjustAssets();
14616         }
14617         this.fireEvent("resize", this, width, height);
14618         return this;
14619     },
14620
14621
14622     /**
14623      * Resizes the dialog to fit the specified content size.
14624      * @param {Number} width
14625      * @param {Number} height
14626      * @return {Roo.BasicDialog} this
14627      */
14628     setContentSize : function(w, h){
14629         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14630         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14631         //if(!this.el.isBorderBox()){
14632             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14633             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14634         //}
14635         if(this.tabs){
14636             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14637             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14638         }
14639         this.resizeTo(w, h);
14640         return this;
14641     },
14642
14643     /**
14644      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14645      * executed in response to a particular key being pressed while the dialog is active.
14646      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14647      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14648      * @param {Function} fn The function to call
14649      * @param {Object} scope (optional) The scope of the function
14650      * @return {Roo.BasicDialog} this
14651      */
14652     addKeyListener : function(key, fn, scope){
14653         var keyCode, shift, ctrl, alt;
14654         if(typeof key == "object" && !(key instanceof Array)){
14655             keyCode = key["key"];
14656             shift = key["shift"];
14657             ctrl = key["ctrl"];
14658             alt = key["alt"];
14659         }else{
14660             keyCode = key;
14661         }
14662         var handler = function(dlg, e){
14663             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14664                 var k = e.getKey();
14665                 if(keyCode instanceof Array){
14666                     for(var i = 0, len = keyCode.length; i < len; i++){
14667                         if(keyCode[i] == k){
14668                           fn.call(scope || window, dlg, k, e);
14669                           return;
14670                         }
14671                     }
14672                 }else{
14673                     if(k == keyCode){
14674                         fn.call(scope || window, dlg, k, e);
14675                     }
14676                 }
14677             }
14678         };
14679         this.on("keydown", handler);
14680         return this;
14681     },
14682
14683     /**
14684      * Returns the TabPanel component (creates it if it doesn't exist).
14685      * Note: If you wish to simply check for the existence of tabs without creating them,
14686      * check for a null 'tabs' property.
14687      * @return {Roo.TabPanel} The tabs component
14688      */
14689     getTabs : function(){
14690         if(!this.tabs){
14691             this.el.addClass("x-dlg-auto-tabs");
14692             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14693             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14694         }
14695         return this.tabs;
14696     },
14697
14698     /**
14699      * Adds a button to the footer section of the dialog.
14700      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14701      * object or a valid Roo.DomHelper element config
14702      * @param {Function} handler The function called when the button is clicked
14703      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14704      * @return {Roo.Button} The new button
14705      */
14706     addButton : function(config, handler, scope){
14707         var dh = Roo.DomHelper;
14708         if(!this.footer){
14709             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14710         }
14711         if(!this.btnContainer){
14712             var tb = this.footer.createChild({
14713
14714                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14715                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14716             }, null, true);
14717             this.btnContainer = tb.firstChild.firstChild.firstChild;
14718         }
14719         var bconfig = {
14720             handler: handler,
14721             scope: scope,
14722             minWidth: this.minButtonWidth,
14723             hideParent:true
14724         };
14725         if(typeof config == "string"){
14726             bconfig.text = config;
14727         }else{
14728             if(config.tag){
14729                 bconfig.dhconfig = config;
14730             }else{
14731                 Roo.apply(bconfig, config);
14732             }
14733         }
14734         var fc = false;
14735         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14736             bconfig.position = Math.max(0, bconfig.position);
14737             fc = this.btnContainer.childNodes[bconfig.position];
14738         }
14739          
14740         var btn = new Roo.Button(
14741             fc ? 
14742                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14743                 : this.btnContainer.appendChild(document.createElement("td")),
14744             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14745             bconfig
14746         );
14747         this.syncBodyHeight();
14748         if(!this.buttons){
14749             /**
14750              * Array of all the buttons that have been added to this dialog via addButton
14751              * @type Array
14752              */
14753             this.buttons = [];
14754         }
14755         this.buttons.push(btn);
14756         return btn;
14757     },
14758
14759     /**
14760      * Sets the default button to be focused when the dialog is displayed.
14761      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14762      * @return {Roo.BasicDialog} this
14763      */
14764     setDefaultButton : function(btn){
14765         this.defaultButton = btn;
14766         return this;
14767     },
14768
14769     // private
14770     getHeaderFooterHeight : function(safe){
14771         var height = 0;
14772         if(this.header){
14773            height += this.header.getHeight();
14774         }
14775         if(this.footer){
14776            var fm = this.footer.getMargins();
14777             height += (this.footer.getHeight()+fm.top+fm.bottom);
14778         }
14779         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14780         height += this.centerBg.getPadding("tb");
14781         return height;
14782     },
14783
14784     // private
14785     syncBodyHeight : function(){
14786         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
14787         var height = this.size.height - this.getHeaderFooterHeight(false);
14788         bd.setHeight(height-bd.getMargins("tb"));
14789         var hh = this.header.getHeight();
14790         var h = this.size.height-hh;
14791         cb.setHeight(h);
14792         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14793         bw.setHeight(h-cb.getPadding("tb"));
14794         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14795         bd.setWidth(bw.getWidth(true));
14796         if(this.tabs){
14797             this.tabs.syncHeight();
14798             if(Roo.isIE){
14799                 this.tabs.el.repaint();
14800             }
14801         }
14802     },
14803
14804     /**
14805      * Restores the previous state of the dialog if Roo.state is configured.
14806      * @return {Roo.BasicDialog} this
14807      */
14808     restoreState : function(){
14809         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14810         if(box && box.width){
14811             this.xy = [box.x, box.y];
14812             this.resizeTo(box.width, box.height);
14813         }
14814         return this;
14815     },
14816
14817     // private
14818     beforeShow : function(){
14819         this.expand();
14820         if(this.fixedcenter){
14821             this.xy = this.el.getCenterXY(true);
14822         }
14823         if(this.modal){
14824             Roo.get(document.body).addClass("x-body-masked");
14825             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14826             this.mask.show();
14827         }
14828         this.constrainXY();
14829     },
14830
14831     // private
14832     animShow : function(){
14833         var b = Roo.get(this.animateTarget).getBox();
14834         this.proxy.setSize(b.width, b.height);
14835         this.proxy.setLocation(b.x, b.y);
14836         this.proxy.show();
14837         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14838                     true, .35, this.showEl.createDelegate(this));
14839     },
14840
14841     /**
14842      * Shows the dialog.
14843      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14844      * @return {Roo.BasicDialog} this
14845      */
14846     show : function(animateTarget){
14847         if (this.fireEvent("beforeshow", this) === false){
14848             return;
14849         }
14850         if(this.syncHeightBeforeShow){
14851             this.syncBodyHeight();
14852         }else if(this.firstShow){
14853             this.firstShow = false;
14854             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14855         }
14856         this.animateTarget = animateTarget || this.animateTarget;
14857         if(!this.el.isVisible()){
14858             this.beforeShow();
14859             if(this.animateTarget && Roo.get(this.animateTarget)){
14860                 this.animShow();
14861             }else{
14862                 this.showEl();
14863             }
14864         }
14865         return this;
14866     },
14867
14868     // private
14869     showEl : function(){
14870         this.proxy.hide();
14871         this.el.setXY(this.xy);
14872         this.el.show();
14873         this.adjustAssets(true);
14874         this.toFront();
14875         this.focus();
14876         // IE peekaboo bug - fix found by Dave Fenwick
14877         if(Roo.isIE){
14878             this.el.repaint();
14879         }
14880         this.fireEvent("show", this);
14881     },
14882
14883     /**
14884      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14885      * dialog itself will receive focus.
14886      */
14887     focus : function(){
14888         if(this.defaultButton){
14889             this.defaultButton.focus();
14890         }else{
14891             this.focusEl.focus();
14892         }
14893     },
14894
14895     // private
14896     constrainXY : function(){
14897         if(this.constraintoviewport !== false){
14898             if(!this.viewSize){
14899                 if(this.container){
14900                     var s = this.container.getSize();
14901                     this.viewSize = [s.width, s.height];
14902                 }else{
14903                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14904                 }
14905             }
14906             var s = Roo.get(this.container||document).getScroll();
14907
14908             var x = this.xy[0], y = this.xy[1];
14909             var w = this.size.width, h = this.size.height;
14910             var vw = this.viewSize[0], vh = this.viewSize[1];
14911             // only move it if it needs it
14912             var moved = false;
14913             // first validate right/bottom
14914             if(x + w > vw+s.left){
14915                 x = vw - w;
14916                 moved = true;
14917             }
14918             if(y + h > vh+s.top){
14919                 y = vh - h;
14920                 moved = true;
14921             }
14922             // then make sure top/left isn't negative
14923             if(x < s.left){
14924                 x = s.left;
14925                 moved = true;
14926             }
14927             if(y < s.top){
14928                 y = s.top;
14929                 moved = true;
14930             }
14931             if(moved){
14932                 // cache xy
14933                 this.xy = [x, y];
14934                 if(this.isVisible()){
14935                     this.el.setLocation(x, y);
14936                     this.adjustAssets();
14937                 }
14938             }
14939         }
14940     },
14941
14942     // private
14943     onDrag : function(){
14944         if(!this.proxyDrag){
14945             this.xy = this.el.getXY();
14946             this.adjustAssets();
14947         }
14948     },
14949
14950     // private
14951     adjustAssets : function(doShow){
14952         var x = this.xy[0], y = this.xy[1];
14953         var w = this.size.width, h = this.size.height;
14954         if(doShow === true){
14955             if(this.shadow){
14956                 this.shadow.show(this.el);
14957             }
14958             if(this.shim){
14959                 this.shim.show();
14960             }
14961         }
14962         if(this.shadow && this.shadow.isVisible()){
14963             this.shadow.show(this.el);
14964         }
14965         if(this.shim && this.shim.isVisible()){
14966             this.shim.setBounds(x, y, w, h);
14967         }
14968     },
14969
14970     // private
14971     adjustViewport : function(w, h){
14972         if(!w || !h){
14973             w = Roo.lib.Dom.getViewWidth();
14974             h = Roo.lib.Dom.getViewHeight();
14975         }
14976         // cache the size
14977         this.viewSize = [w, h];
14978         if(this.modal && this.mask.isVisible()){
14979             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14980             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14981         }
14982         if(this.isVisible()){
14983             this.constrainXY();
14984         }
14985     },
14986
14987     /**
14988      * Destroys this dialog and all its supporting elements (including any tabs, shim,
14989      * shadow, proxy, mask, etc.)  Also removes all event listeners.
14990      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14991      */
14992     destroy : function(removeEl){
14993         if(this.isVisible()){
14994             this.animateTarget = null;
14995             this.hide();
14996         }
14997         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14998         if(this.tabs){
14999             this.tabs.destroy(removeEl);
15000         }
15001         Roo.destroy(
15002              this.shim,
15003              this.proxy,
15004              this.resizer,
15005              this.close,
15006              this.mask
15007         );
15008         if(this.dd){
15009             this.dd.unreg();
15010         }
15011         if(this.buttons){
15012            for(var i = 0, len = this.buttons.length; i < len; i++){
15013                this.buttons[i].destroy();
15014            }
15015         }
15016         this.el.removeAllListeners();
15017         if(removeEl === true){
15018             this.el.update("");
15019             this.el.remove();
15020         }
15021         Roo.DialogManager.unregister(this);
15022     },
15023
15024     // private
15025     startMove : function(){
15026         if(this.proxyDrag){
15027             this.proxy.show();
15028         }
15029         if(this.constraintoviewport !== false){
15030             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
15031         }
15032     },
15033
15034     // private
15035     endMove : function(){
15036         if(!this.proxyDrag){
15037             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
15038         }else{
15039             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
15040             this.proxy.hide();
15041         }
15042         this.refreshSize();
15043         this.adjustAssets();
15044         this.focus();
15045         this.fireEvent("move", this, this.xy[0], this.xy[1]);
15046     },
15047
15048     /**
15049      * Brings this dialog to the front of any other visible dialogs
15050      * @return {Roo.BasicDialog} this
15051      */
15052     toFront : function(){
15053         Roo.DialogManager.bringToFront(this);
15054         return this;
15055     },
15056
15057     /**
15058      * Sends this dialog to the back (under) of any other visible dialogs
15059      * @return {Roo.BasicDialog} this
15060      */
15061     toBack : function(){
15062         Roo.DialogManager.sendToBack(this);
15063         return this;
15064     },
15065
15066     /**
15067      * Centers this dialog in the viewport
15068      * @return {Roo.BasicDialog} this
15069      */
15070     center : function(){
15071         var xy = this.el.getCenterXY(true);
15072         this.moveTo(xy[0], xy[1]);
15073         return this;
15074     },
15075
15076     /**
15077      * Moves the dialog's top-left corner to the specified point
15078      * @param {Number} x
15079      * @param {Number} y
15080      * @return {Roo.BasicDialog} this
15081      */
15082     moveTo : function(x, y){
15083         this.xy = [x,y];
15084         if(this.isVisible()){
15085             this.el.setXY(this.xy);
15086             this.adjustAssets();
15087         }
15088         return this;
15089     },
15090
15091     /**
15092      * Aligns the dialog to the specified element
15093      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15094      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
15095      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15096      * @return {Roo.BasicDialog} this
15097      */
15098     alignTo : function(element, position, offsets){
15099         this.xy = this.el.getAlignToXY(element, position, offsets);
15100         if(this.isVisible()){
15101             this.el.setXY(this.xy);
15102             this.adjustAssets();
15103         }
15104         return this;
15105     },
15106
15107     /**
15108      * Anchors an element to another element and realigns it when the window is resized.
15109      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15110      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
15111      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15112      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
15113      * is a number, it is used as the buffer delay (defaults to 50ms).
15114      * @return {Roo.BasicDialog} this
15115      */
15116     anchorTo : function(el, alignment, offsets, monitorScroll){
15117         var action = function(){
15118             this.alignTo(el, alignment, offsets);
15119         };
15120         Roo.EventManager.onWindowResize(action, this);
15121         var tm = typeof monitorScroll;
15122         if(tm != 'undefined'){
15123             Roo.EventManager.on(window, 'scroll', action, this,
15124                 {buffer: tm == 'number' ? monitorScroll : 50});
15125         }
15126         action.call(this);
15127         return this;
15128     },
15129
15130     /**
15131      * Returns true if the dialog is visible
15132      * @return {Boolean}
15133      */
15134     isVisible : function(){
15135         return this.el.isVisible();
15136     },
15137
15138     // private
15139     animHide : function(callback){
15140         var b = Roo.get(this.animateTarget).getBox();
15141         this.proxy.show();
15142         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
15143         this.el.hide();
15144         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
15145                     this.hideEl.createDelegate(this, [callback]));
15146     },
15147
15148     /**
15149      * Hides the dialog.
15150      * @param {Function} callback (optional) Function to call when the dialog is hidden
15151      * @return {Roo.BasicDialog} this
15152      */
15153     hide : function(callback){
15154         if (this.fireEvent("beforehide", this) === false){
15155             return;
15156         }
15157         if(this.shadow){
15158             this.shadow.hide();
15159         }
15160         if(this.shim) {
15161           this.shim.hide();
15162         }
15163         // sometimes animateTarget seems to get set.. causing problems...
15164         // this just double checks..
15165         if(this.animateTarget && Roo.get(this.animateTarget)) {
15166            this.animHide(callback);
15167         }else{
15168             this.el.hide();
15169             this.hideEl(callback);
15170         }
15171         return this;
15172     },
15173
15174     // private
15175     hideEl : function(callback){
15176         this.proxy.hide();
15177         if(this.modal){
15178             this.mask.hide();
15179             Roo.get(document.body).removeClass("x-body-masked");
15180         }
15181         this.fireEvent("hide", this);
15182         if(typeof callback == "function"){
15183             callback();
15184         }
15185     },
15186
15187     // private
15188     hideAction : function(){
15189         this.setLeft("-10000px");
15190         this.setTop("-10000px");
15191         this.setStyle("visibility", "hidden");
15192     },
15193
15194     // private
15195     refreshSize : function(){
15196         this.size = this.el.getSize();
15197         this.xy = this.el.getXY();
15198         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15199     },
15200
15201     // private
15202     // z-index is managed by the DialogManager and may be overwritten at any time
15203     setZIndex : function(index){
15204         if(this.modal){
15205             this.mask.setStyle("z-index", index);
15206         }
15207         if(this.shim){
15208             this.shim.setStyle("z-index", ++index);
15209         }
15210         if(this.shadow){
15211             this.shadow.setZIndex(++index);
15212         }
15213         this.el.setStyle("z-index", ++index);
15214         if(this.proxy){
15215             this.proxy.setStyle("z-index", ++index);
15216         }
15217         if(this.resizer){
15218             this.resizer.proxy.setStyle("z-index", ++index);
15219         }
15220
15221         this.lastZIndex = index;
15222     },
15223
15224     /**
15225      * Returns the element for this dialog
15226      * @return {Roo.Element} The underlying dialog Element
15227      */
15228     getEl : function(){
15229         return this.el;
15230     }
15231 });
15232
15233 /**
15234  * @class Roo.DialogManager
15235  * Provides global access to BasicDialogs that have been created and
15236  * support for z-indexing (layering) multiple open dialogs.
15237  */
15238 Roo.DialogManager = function(){
15239     var list = {};
15240     var accessList = [];
15241     var front = null;
15242
15243     // private
15244     var sortDialogs = function(d1, d2){
15245         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15246     };
15247
15248     // private
15249     var orderDialogs = function(){
15250         accessList.sort(sortDialogs);
15251         var seed = Roo.DialogManager.zseed;
15252         for(var i = 0, len = accessList.length; i < len; i++){
15253             var dlg = accessList[i];
15254             if(dlg){
15255                 dlg.setZIndex(seed + (i*10));
15256             }
15257         }
15258     };
15259
15260     return {
15261         /**
15262          * The starting z-index for BasicDialogs (defaults to 9000)
15263          * @type Number The z-index value
15264          */
15265         zseed : 9000,
15266
15267         // private
15268         register : function(dlg){
15269             list[dlg.id] = dlg;
15270             accessList.push(dlg);
15271         },
15272
15273         // private
15274         unregister : function(dlg){
15275             delete list[dlg.id];
15276             var i=0;
15277             var len=0;
15278             if(!accessList.indexOf){
15279                 for(  i = 0, len = accessList.length; i < len; i++){
15280                     if(accessList[i] == dlg){
15281                         accessList.splice(i, 1);
15282                         return;
15283                     }
15284                 }
15285             }else{
15286                  i = accessList.indexOf(dlg);
15287                 if(i != -1){
15288                     accessList.splice(i, 1);
15289                 }
15290             }
15291         },
15292
15293         /**
15294          * Gets a registered dialog by id
15295          * @param {String/Object} id The id of the dialog or a dialog
15296          * @return {Roo.BasicDialog} this
15297          */
15298         get : function(id){
15299             return typeof id == "object" ? id : list[id];
15300         },
15301
15302         /**
15303          * Brings the specified dialog to the front
15304          * @param {String/Object} dlg The id of the dialog or a dialog
15305          * @return {Roo.BasicDialog} this
15306          */
15307         bringToFront : function(dlg){
15308             dlg = this.get(dlg);
15309             if(dlg != front){
15310                 front = dlg;
15311                 dlg._lastAccess = new Date().getTime();
15312                 orderDialogs();
15313             }
15314             return dlg;
15315         },
15316
15317         /**
15318          * Sends the specified dialog to the back
15319          * @param {String/Object} dlg The id of the dialog or a dialog
15320          * @return {Roo.BasicDialog} this
15321          */
15322         sendToBack : function(dlg){
15323             dlg = this.get(dlg);
15324             dlg._lastAccess = -(new Date().getTime());
15325             orderDialogs();
15326             return dlg;
15327         },
15328
15329         /**
15330          * Hides all dialogs
15331          */
15332         hideAll : function(){
15333             for(var id in list){
15334                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15335                     list[id].hide();
15336                 }
15337             }
15338         }
15339     };
15340 }();
15341
15342 /**
15343  * @class Roo.LayoutDialog
15344  * @extends Roo.BasicDialog
15345  * Dialog which provides adjustments for working with a layout in a Dialog.
15346  * Add your necessary layout config options to the dialog's config.<br>
15347  * Example usage (including a nested layout):
15348  * <pre><code>
15349 if(!dialog){
15350     dialog = new Roo.LayoutDialog("download-dlg", {
15351         modal: true,
15352         width:600,
15353         height:450,
15354         shadow:true,
15355         minWidth:500,
15356         minHeight:350,
15357         autoTabs:true,
15358         proxyDrag:true,
15359         // layout config merges with the dialog config
15360         center:{
15361             tabPosition: "top",
15362             alwaysShowTabs: true
15363         }
15364     });
15365     dialog.addKeyListener(27, dialog.hide, dialog);
15366     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15367     dialog.addButton("Build It!", this.getDownload, this);
15368
15369     // we can even add nested layouts
15370     var innerLayout = new Roo.BorderLayout("dl-inner", {
15371         east: {
15372             initialSize: 200,
15373             autoScroll:true,
15374             split:true
15375         },
15376         center: {
15377             autoScroll:true
15378         }
15379     });
15380     innerLayout.beginUpdate();
15381     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15382     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15383     innerLayout.endUpdate(true);
15384
15385     var layout = dialog.getLayout();
15386     layout.beginUpdate();
15387     layout.add("center", new Roo.ContentPanel("standard-panel",
15388                         {title: "Download the Source", fitToFrame:true}));
15389     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15390                {title: "Build your own roo.js"}));
15391     layout.getRegion("center").showPanel(sp);
15392     layout.endUpdate();
15393 }
15394 </code></pre>
15395     * @constructor
15396     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15397     * @param {Object} config configuration options
15398   */
15399 Roo.LayoutDialog = function(el, cfg){
15400     
15401     var config=  cfg;
15402     if (typeof(cfg) == 'undefined') {
15403         config = Roo.apply({}, el);
15404         // not sure why we use documentElement here.. - it should always be body.
15405         // IE7 borks horribly if we use documentElement.
15406         // webkit also does not like documentElement - it creates a body element...
15407         el = Roo.get( document.body || document.documentElement ).createChild();
15408         //config.autoCreate = true;
15409     }
15410     
15411     
15412     config.autoTabs = false;
15413     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15414     this.body.setStyle({overflow:"hidden", position:"relative"});
15415     this.layout = new Roo.BorderLayout(this.body.dom, config);
15416     this.layout.monitorWindowResize = false;
15417     this.el.addClass("x-dlg-auto-layout");
15418     // fix case when center region overwrites center function
15419     this.center = Roo.BasicDialog.prototype.center;
15420     this.on("show", this.layout.layout, this.layout, true);
15421     if (config.items) {
15422         var xitems = config.items;
15423         delete config.items;
15424         Roo.each(xitems, this.addxtype, this);
15425     }
15426     
15427     
15428 };
15429 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15430     /**
15431      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15432      * @deprecated
15433      */
15434     endUpdate : function(){
15435         this.layout.endUpdate();
15436     },
15437
15438     /**
15439      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15440      *  @deprecated
15441      */
15442     beginUpdate : function(){
15443         this.layout.beginUpdate();
15444     },
15445
15446     /**
15447      * Get the BorderLayout for this dialog
15448      * @return {Roo.BorderLayout}
15449      */
15450     getLayout : function(){
15451         return this.layout;
15452     },
15453
15454     showEl : function(){
15455         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15456         if(Roo.isIE7){
15457             this.layout.layout();
15458         }
15459     },
15460
15461     // private
15462     // Use the syncHeightBeforeShow config option to control this automatically
15463     syncBodyHeight : function(){
15464         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15465         if(this.layout){this.layout.layout();}
15466     },
15467     
15468       /**
15469      * Add an xtype element (actually adds to the layout.)
15470      * @return {Object} xdata xtype object data.
15471      */
15472     
15473     addxtype : function(c) {
15474         return this.layout.addxtype(c);
15475     }
15476 });/*
15477  * Based on:
15478  * Ext JS Library 1.1.1
15479  * Copyright(c) 2006-2007, Ext JS, LLC.
15480  *
15481  * Originally Released Under LGPL - original licence link has changed is not relivant.
15482  *
15483  * Fork - LGPL
15484  * <script type="text/javascript">
15485  */
15486  
15487 /**
15488  * @class Roo.MessageBox
15489  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15490  * Example usage:
15491  *<pre><code>
15492 // Basic alert:
15493 Roo.Msg.alert('Status', 'Changes saved successfully.');
15494
15495 // Prompt for user data:
15496 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15497     if (btn == 'ok'){
15498         // process text value...
15499     }
15500 });
15501
15502 // Show a dialog using config options:
15503 Roo.Msg.show({
15504    title:'Save Changes?',
15505    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15506    buttons: Roo.Msg.YESNOCANCEL,
15507    fn: processResult,
15508    animEl: 'elId'
15509 });
15510 </code></pre>
15511  * @singleton
15512  */
15513 Roo.MessageBox = function(){
15514     var dlg, opt, mask, waitTimer;
15515     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15516     var buttons, activeTextEl, bwidth;
15517
15518     // private
15519     var handleButton = function(button){
15520         dlg.hide();
15521         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15522     };
15523
15524     // private
15525     var handleHide = function(){
15526         if(opt && opt.cls){
15527             dlg.el.removeClass(opt.cls);
15528         }
15529         if(waitTimer){
15530             Roo.TaskMgr.stop(waitTimer);
15531             waitTimer = null;
15532         }
15533     };
15534
15535     // private
15536     var updateButtons = function(b){
15537         var width = 0;
15538         if(!b){
15539             buttons["ok"].hide();
15540             buttons["cancel"].hide();
15541             buttons["yes"].hide();
15542             buttons["no"].hide();
15543             dlg.footer.dom.style.display = 'none';
15544             return width;
15545         }
15546         dlg.footer.dom.style.display = '';
15547         for(var k in buttons){
15548             if(typeof buttons[k] != "function"){
15549                 if(b[k]){
15550                     buttons[k].show();
15551                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15552                     width += buttons[k].el.getWidth()+15;
15553                 }else{
15554                     buttons[k].hide();
15555                 }
15556             }
15557         }
15558         return width;
15559     };
15560
15561     // private
15562     var handleEsc = function(d, k, e){
15563         if(opt && opt.closable !== false){
15564             dlg.hide();
15565         }
15566         if(e){
15567             e.stopEvent();
15568         }
15569     };
15570
15571     return {
15572         /**
15573          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15574          * @return {Roo.BasicDialog} The BasicDialog element
15575          */
15576         getDialog : function(){
15577            if(!dlg){
15578                 dlg = new Roo.BasicDialog("x-msg-box", {
15579                     autoCreate : true,
15580                     shadow: true,
15581                     draggable: true,
15582                     resizable:false,
15583                     constraintoviewport:false,
15584                     fixedcenter:true,
15585                     collapsible : false,
15586                     shim:true,
15587                     modal: true,
15588                     width:400, height:100,
15589                     buttonAlign:"center",
15590                     closeClick : function(){
15591                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15592                             handleButton("no");
15593                         }else{
15594                             handleButton("cancel");
15595                         }
15596                     }
15597                 });
15598                 dlg.on("hide", handleHide);
15599                 mask = dlg.mask;
15600                 dlg.addKeyListener(27, handleEsc);
15601                 buttons = {};
15602                 var bt = this.buttonText;
15603                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15604                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15605                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15606                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15607                 bodyEl = dlg.body.createChild({
15608
15609                     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>'
15610                 });
15611                 msgEl = bodyEl.dom.firstChild;
15612                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15613                 textboxEl.enableDisplayMode();
15614                 textboxEl.addKeyListener([10,13], function(){
15615                     if(dlg.isVisible() && opt && opt.buttons){
15616                         if(opt.buttons.ok){
15617                             handleButton("ok");
15618                         }else if(opt.buttons.yes){
15619                             handleButton("yes");
15620                         }
15621                     }
15622                 });
15623                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15624                 textareaEl.enableDisplayMode();
15625                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15626                 progressEl.enableDisplayMode();
15627                 var pf = progressEl.dom.firstChild;
15628                 if (pf) {
15629                     pp = Roo.get(pf.firstChild);
15630                     pp.setHeight(pf.offsetHeight);
15631                 }
15632                 
15633             }
15634             return dlg;
15635         },
15636
15637         /**
15638          * Updates the message box body text
15639          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15640          * the XHTML-compliant non-breaking space character '&amp;#160;')
15641          * @return {Roo.MessageBox} This message box
15642          */
15643         updateText : function(text){
15644             if(!dlg.isVisible() && !opt.width){
15645                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15646             }
15647             msgEl.innerHTML = text || '&#160;';
15648             this.updateTextSize.defer(100, this);
15649             return this;
15650         },
15651         
15652         updateTextSize: function()
15653         {
15654         
15655             var cw =  Math.max(msgEl.offsetWidth, bodyEl.scrollWidth);
15656             Roo.log("guesed size: " + cw);
15657             var w = Math.max(
15658                     Math.min(opt.width || cw , this.maxWidth), 
15659                     Math.max(opt.minWidth || this.minWidth, bwidth)
15660             );
15661             if(opt.prompt){
15662                 activeTextEl.setWidth(w);
15663             }
15664             if(dlg.isVisible()){
15665                 dlg.fixedcenter = false;
15666             }
15667             // to big, make it scoll.
15668             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
15669                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
15670                 bodyEl.dom.style.overflowY = 'auto !important';
15671             } else {
15672                 bodyEl.dom.style.height = '';
15673                 bodyEl.dom.style.overflowY = '';
15674             }
15675             if (cw > w) {
15676                 bodyEl.dom.style.overflowX = 'auto !important';
15677             } else {
15678                 bodyEl.dom.style.overflowX = '';
15679             }
15680             
15681             dlg.setContentSize(w, bodyEl.getHeight());
15682             if(dlg.isVisible()){
15683                 dlg.fixedcenter = true;
15684             }
15685             return this;
15686         },
15687
15688         /**
15689          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15690          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15691          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15692          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15693          * @return {Roo.MessageBox} This message box
15694          */
15695         updateProgress : function(value, text){
15696             if(text){
15697                 this.updateText(text);
15698             }
15699             if (pp) { // weird bug on my firefox - for some reason this is not defined
15700                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15701             }
15702             return this;
15703         },        
15704
15705         /**
15706          * Returns true if the message box is currently displayed
15707          * @return {Boolean} True if the message box is visible, else false
15708          */
15709         isVisible : function(){
15710             return dlg && dlg.isVisible();  
15711         },
15712
15713         /**
15714          * Hides the message box if it is displayed
15715          */
15716         hide : function(){
15717             if(this.isVisible()){
15718                 dlg.hide();
15719             }  
15720         },
15721
15722         /**
15723          * Displays a new message box, or reinitializes an existing message box, based on the config options
15724          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15725          * The following config object properties are supported:
15726          * <pre>
15727 Property    Type             Description
15728 ----------  ---------------  ------------------------------------------------------------------------------------
15729 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15730                                    closes (defaults to undefined)
15731 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15732                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15733 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15734                                    progress and wait dialogs will ignore this property and always hide the
15735                                    close button as they can only be closed programmatically.
15736 cls               String           A custom CSS class to apply to the message box element
15737 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15738                                    displayed (defaults to 75)
15739 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15740                                    function will be btn (the name of the button that was clicked, if applicable,
15741                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15742                                    Progress and wait dialogs will ignore this option since they do not respond to
15743                                    user actions and can only be closed programmatically, so any required function
15744                                    should be called by the same code after it closes the dialog.
15745 icon              String           A CSS class that provides a background image to be used as an icon for
15746                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15747 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15748 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15749 modal             Boolean          False to allow user interaction with the page while the message box is
15750                                    displayed (defaults to true)
15751 msg               String           A string that will replace the existing message box body text (defaults
15752                                    to the XHTML-compliant non-breaking space character '&#160;')
15753 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15754 progress          Boolean          True to display a progress bar (defaults to false)
15755 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15756 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15757 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15758 title             String           The title text
15759 value             String           The string value to set into the active textbox element if displayed
15760 wait              Boolean          True to display a progress bar (defaults to false)
15761 width             Number           The width of the dialog in pixels
15762 </pre>
15763          *
15764          * Example usage:
15765          * <pre><code>
15766 Roo.Msg.show({
15767    title: 'Address',
15768    msg: 'Please enter your address:',
15769    width: 300,
15770    buttons: Roo.MessageBox.OKCANCEL,
15771    multiline: true,
15772    fn: saveAddress,
15773    animEl: 'addAddressBtn'
15774 });
15775 </code></pre>
15776          * @param {Object} config Configuration options
15777          * @return {Roo.MessageBox} This message box
15778          */
15779         show : function(options)
15780         {
15781             
15782             // this causes nightmares if you show one dialog after another
15783             // especially on callbacks..
15784              
15785             if(this.isVisible()){
15786                 
15787                 this.hide();
15788                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML )
15789                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
15790                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
15791                 
15792             }
15793             var d = this.getDialog();
15794             opt = options;
15795             d.setTitle(opt.title || "&#160;");
15796             d.close.setDisplayed(opt.closable !== false);
15797             activeTextEl = textboxEl;
15798             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15799             if(opt.prompt){
15800                 if(opt.multiline){
15801                     textboxEl.hide();
15802                     textareaEl.show();
15803                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15804                         opt.multiline : this.defaultTextHeight);
15805                     activeTextEl = textareaEl;
15806                 }else{
15807                     textboxEl.show();
15808                     textareaEl.hide();
15809                 }
15810             }else{
15811                 textboxEl.hide();
15812                 textareaEl.hide();
15813             }
15814             progressEl.setDisplayed(opt.progress === true);
15815             this.updateProgress(0);
15816             activeTextEl.dom.value = opt.value || "";
15817             if(opt.prompt){
15818                 dlg.setDefaultButton(activeTextEl);
15819             }else{
15820                 var bs = opt.buttons;
15821                 var db = null;
15822                 if(bs && bs.ok){
15823                     db = buttons["ok"];
15824                 }else if(bs && bs.yes){
15825                     db = buttons["yes"];
15826                 }
15827                 dlg.setDefaultButton(db);
15828             }
15829             bwidth = updateButtons(opt.buttons);
15830             this.updateText(opt.msg);
15831             if(opt.cls){
15832                 d.el.addClass(opt.cls);
15833             }
15834             d.proxyDrag = opt.proxyDrag === true;
15835             d.modal = opt.modal !== false;
15836             d.mask = opt.modal !== false ? mask : false;
15837             if(!d.isVisible()){
15838                 // force it to the end of the z-index stack so it gets a cursor in FF
15839                 document.body.appendChild(dlg.el.dom);
15840                 d.animateTarget = null;
15841                 d.show(options.animEl);
15842             }
15843             return this;
15844         },
15845
15846         /**
15847          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15848          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15849          * and closing the message box when the process is complete.
15850          * @param {String} title The title bar text
15851          * @param {String} msg The message box body text
15852          * @return {Roo.MessageBox} This message box
15853          */
15854         progress : function(title, msg){
15855             this.show({
15856                 title : title,
15857                 msg : msg,
15858                 buttons: false,
15859                 progress:true,
15860                 closable:false,
15861                 minWidth: this.minProgressWidth,
15862                 modal : true
15863             });
15864             return this;
15865         },
15866
15867         /**
15868          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15869          * If a callback function is passed it will be called after the user clicks the button, and the
15870          * id of the button that was clicked will be passed as the only parameter to the callback
15871          * (could also be the top-right close button).
15872          * @param {String} title The title bar text
15873          * @param {String} msg The message box body text
15874          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15875          * @param {Object} scope (optional) The scope of the callback function
15876          * @return {Roo.MessageBox} This message box
15877          */
15878         alert : function(title, msg, fn, scope){
15879             this.show({
15880                 title : title,
15881                 msg : msg,
15882                 buttons: this.OK,
15883                 fn: fn,
15884                 scope : scope,
15885                 modal : true
15886             });
15887             return this;
15888         },
15889
15890         /**
15891          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15892          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15893          * You are responsible for closing the message box when the process is complete.
15894          * @param {String} msg The message box body text
15895          * @param {String} title (optional) The title bar text
15896          * @return {Roo.MessageBox} This message box
15897          */
15898         wait : function(msg, title){
15899             this.show({
15900                 title : title,
15901                 msg : msg,
15902                 buttons: false,
15903                 closable:false,
15904                 progress:true,
15905                 modal:true,
15906                 width:300,
15907                 wait:true
15908             });
15909             waitTimer = Roo.TaskMgr.start({
15910                 run: function(i){
15911                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15912                 },
15913                 interval: 1000
15914             });
15915             return this;
15916         },
15917
15918         /**
15919          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15920          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15921          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15922          * @param {String} title The title bar text
15923          * @param {String} msg The message box body text
15924          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15925          * @param {Object} scope (optional) The scope of the callback function
15926          * @return {Roo.MessageBox} This message box
15927          */
15928         confirm : function(title, msg, fn, scope){
15929             this.show({
15930                 title : title,
15931                 msg : msg,
15932                 buttons: this.YESNO,
15933                 fn: fn,
15934                 scope : scope,
15935                 modal : true
15936             });
15937             return this;
15938         },
15939
15940         /**
15941          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15942          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15943          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15944          * (could also be the top-right close button) and the text that was entered will be passed as the two
15945          * parameters to the callback.
15946          * @param {String} title The title bar text
15947          * @param {String} msg The message box body text
15948          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15949          * @param {Object} scope (optional) The scope of the callback function
15950          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15951          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15952          * @return {Roo.MessageBox} This message box
15953          */
15954         prompt : function(title, msg, fn, scope, multiline){
15955             this.show({
15956                 title : title,
15957                 msg : msg,
15958                 buttons: this.OKCANCEL,
15959                 fn: fn,
15960                 minWidth:250,
15961                 scope : scope,
15962                 prompt:true,
15963                 multiline: multiline,
15964                 modal : true
15965             });
15966             return this;
15967         },
15968
15969         /**
15970          * Button config that displays a single OK button
15971          * @type Object
15972          */
15973         OK : {ok:true},
15974         /**
15975          * Button config that displays Yes and No buttons
15976          * @type Object
15977          */
15978         YESNO : {yes:true, no:true},
15979         /**
15980          * Button config that displays OK and Cancel buttons
15981          * @type Object
15982          */
15983         OKCANCEL : {ok:true, cancel:true},
15984         /**
15985          * Button config that displays Yes, No and Cancel buttons
15986          * @type Object
15987          */
15988         YESNOCANCEL : {yes:true, no:true, cancel:true},
15989
15990         /**
15991          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15992          * @type Number
15993          */
15994         defaultTextHeight : 75,
15995         /**
15996          * The maximum width in pixels of the message box (defaults to 600)
15997          * @type Number
15998          */
15999         maxWidth : 600,
16000         /**
16001          * The minimum width in pixels of the message box (defaults to 100)
16002          * @type Number
16003          */
16004         minWidth : 100,
16005         /**
16006          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
16007          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
16008          * @type Number
16009          */
16010         minProgressWidth : 250,
16011         /**
16012          * An object containing the default button text strings that can be overriden for localized language support.
16013          * Supported properties are: ok, cancel, yes and no.
16014          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
16015          * @type Object
16016          */
16017         buttonText : {
16018             ok : "OK",
16019             cancel : "Cancel",
16020             yes : "Yes",
16021             no : "No"
16022         }
16023     };
16024 }();
16025
16026 /**
16027  * Shorthand for {@link Roo.MessageBox}
16028  */
16029 Roo.Msg = Roo.MessageBox;/*
16030  * Based on:
16031  * Ext JS Library 1.1.1
16032  * Copyright(c) 2006-2007, Ext JS, LLC.
16033  *
16034  * Originally Released Under LGPL - original licence link has changed is not relivant.
16035  *
16036  * Fork - LGPL
16037  * <script type="text/javascript">
16038  */
16039 /**
16040  * @class Roo.QuickTips
16041  * Provides attractive and customizable tooltips for any element.
16042  * @singleton
16043  */
16044 Roo.QuickTips = function(){
16045     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
16046     var ce, bd, xy, dd;
16047     var visible = false, disabled = true, inited = false;
16048     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
16049     
16050     var onOver = function(e){
16051         if(disabled){
16052             return;
16053         }
16054         var t = e.getTarget();
16055         if(!t || t.nodeType !== 1 || t == document || t == document.body){
16056             return;
16057         }
16058         if(ce && t == ce.el){
16059             clearTimeout(hideProc);
16060             return;
16061         }
16062         if(t && tagEls[t.id]){
16063             tagEls[t.id].el = t;
16064             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
16065             return;
16066         }
16067         var ttp, et = Roo.fly(t);
16068         var ns = cfg.namespace;
16069         if(tm.interceptTitles && t.title){
16070             ttp = t.title;
16071             t.qtip = ttp;
16072             t.removeAttribute("title");
16073             e.preventDefault();
16074         }else{
16075             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
16076         }
16077         if(ttp){
16078             showProc = show.defer(tm.showDelay, tm, [{
16079                 el: t, 
16080                 text: ttp, 
16081                 width: et.getAttributeNS(ns, cfg.width),
16082                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
16083                 title: et.getAttributeNS(ns, cfg.title),
16084                     cls: et.getAttributeNS(ns, cfg.cls)
16085             }]);
16086         }
16087     };
16088     
16089     var onOut = function(e){
16090         clearTimeout(showProc);
16091         var t = e.getTarget();
16092         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
16093             hideProc = setTimeout(hide, tm.hideDelay);
16094         }
16095     };
16096     
16097     var onMove = function(e){
16098         if(disabled){
16099             return;
16100         }
16101         xy = e.getXY();
16102         xy[1] += 18;
16103         if(tm.trackMouse && ce){
16104             el.setXY(xy);
16105         }
16106     };
16107     
16108     var onDown = function(e){
16109         clearTimeout(showProc);
16110         clearTimeout(hideProc);
16111         if(!e.within(el)){
16112             if(tm.hideOnClick){
16113                 hide();
16114                 tm.disable();
16115                 tm.enable.defer(100, tm);
16116             }
16117         }
16118     };
16119     
16120     var getPad = function(){
16121         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
16122     };
16123
16124     var show = function(o){
16125         if(disabled){
16126             return;
16127         }
16128         clearTimeout(dismissProc);
16129         ce = o;
16130         if(removeCls){ // in case manually hidden
16131             el.removeClass(removeCls);
16132             removeCls = null;
16133         }
16134         if(ce.cls){
16135             el.addClass(ce.cls);
16136             removeCls = ce.cls;
16137         }
16138         if(ce.title){
16139             tipTitle.update(ce.title);
16140             tipTitle.show();
16141         }else{
16142             tipTitle.update('');
16143             tipTitle.hide();
16144         }
16145         el.dom.style.width  = tm.maxWidth+'px';
16146         //tipBody.dom.style.width = '';
16147         tipBodyText.update(o.text);
16148         var p = getPad(), w = ce.width;
16149         if(!w){
16150             var td = tipBodyText.dom;
16151             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
16152             if(aw > tm.maxWidth){
16153                 w = tm.maxWidth;
16154             }else if(aw < tm.minWidth){
16155                 w = tm.minWidth;
16156             }else{
16157                 w = aw;
16158             }
16159         }
16160         //tipBody.setWidth(w);
16161         el.setWidth(parseInt(w, 10) + p);
16162         if(ce.autoHide === false){
16163             close.setDisplayed(true);
16164             if(dd){
16165                 dd.unlock();
16166             }
16167         }else{
16168             close.setDisplayed(false);
16169             if(dd){
16170                 dd.lock();
16171             }
16172         }
16173         if(xy){
16174             el.avoidY = xy[1]-18;
16175             el.setXY(xy);
16176         }
16177         if(tm.animate){
16178             el.setOpacity(.1);
16179             el.setStyle("visibility", "visible");
16180             el.fadeIn({callback: afterShow});
16181         }else{
16182             afterShow();
16183         }
16184     };
16185     
16186     var afterShow = function(){
16187         if(ce){
16188             el.show();
16189             esc.enable();
16190             if(tm.autoDismiss && ce.autoHide !== false){
16191                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16192             }
16193         }
16194     };
16195     
16196     var hide = function(noanim){
16197         clearTimeout(dismissProc);
16198         clearTimeout(hideProc);
16199         ce = null;
16200         if(el.isVisible()){
16201             esc.disable();
16202             if(noanim !== true && tm.animate){
16203                 el.fadeOut({callback: afterHide});
16204             }else{
16205                 afterHide();
16206             } 
16207         }
16208     };
16209     
16210     var afterHide = function(){
16211         el.hide();
16212         if(removeCls){
16213             el.removeClass(removeCls);
16214             removeCls = null;
16215         }
16216     };
16217     
16218     return {
16219         /**
16220         * @cfg {Number} minWidth
16221         * The minimum width of the quick tip (defaults to 40)
16222         */
16223        minWidth : 40,
16224         /**
16225         * @cfg {Number} maxWidth
16226         * The maximum width of the quick tip (defaults to 300)
16227         */
16228        maxWidth : 300,
16229         /**
16230         * @cfg {Boolean} interceptTitles
16231         * True to automatically use the element's DOM title value if available (defaults to false)
16232         */
16233        interceptTitles : false,
16234         /**
16235         * @cfg {Boolean} trackMouse
16236         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16237         */
16238        trackMouse : false,
16239         /**
16240         * @cfg {Boolean} hideOnClick
16241         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16242         */
16243        hideOnClick : true,
16244         /**
16245         * @cfg {Number} showDelay
16246         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16247         */
16248        showDelay : 500,
16249         /**
16250         * @cfg {Number} hideDelay
16251         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16252         */
16253        hideDelay : 200,
16254         /**
16255         * @cfg {Boolean} autoHide
16256         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16257         * Used in conjunction with hideDelay.
16258         */
16259        autoHide : true,
16260         /**
16261         * @cfg {Boolean}
16262         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16263         * (defaults to true).  Used in conjunction with autoDismissDelay.
16264         */
16265        autoDismiss : true,
16266         /**
16267         * @cfg {Number}
16268         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16269         */
16270        autoDismissDelay : 5000,
16271        /**
16272         * @cfg {Boolean} animate
16273         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16274         */
16275        animate : false,
16276
16277        /**
16278         * @cfg {String} title
16279         * Title text to display (defaults to '').  This can be any valid HTML markup.
16280         */
16281         title: '',
16282        /**
16283         * @cfg {String} text
16284         * Body text to display (defaults to '').  This can be any valid HTML markup.
16285         */
16286         text : '',
16287        /**
16288         * @cfg {String} cls
16289         * A CSS class to apply to the base quick tip element (defaults to '').
16290         */
16291         cls : '',
16292        /**
16293         * @cfg {Number} width
16294         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16295         * minWidth or maxWidth.
16296         */
16297         width : null,
16298
16299     /**
16300      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16301      * or display QuickTips in a page.
16302      */
16303        init : function(){
16304           tm = Roo.QuickTips;
16305           cfg = tm.tagConfig;
16306           if(!inited){
16307               if(!Roo.isReady){ // allow calling of init() before onReady
16308                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16309                   return;
16310               }
16311               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16312               el.fxDefaults = {stopFx: true};
16313               // maximum custom styling
16314               //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>');
16315               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>');              
16316               tipTitle = el.child('h3');
16317               tipTitle.enableDisplayMode("block");
16318               tipBody = el.child('div.x-tip-bd');
16319               tipBodyText = el.child('div.x-tip-bd-inner');
16320               //bdLeft = el.child('div.x-tip-bd-left');
16321               //bdRight = el.child('div.x-tip-bd-right');
16322               close = el.child('div.x-tip-close');
16323               close.enableDisplayMode("block");
16324               close.on("click", hide);
16325               var d = Roo.get(document);
16326               d.on("mousedown", onDown);
16327               d.on("mouseover", onOver);
16328               d.on("mouseout", onOut);
16329               d.on("mousemove", onMove);
16330               esc = d.addKeyListener(27, hide);
16331               esc.disable();
16332               if(Roo.dd.DD){
16333                   dd = el.initDD("default", null, {
16334                       onDrag : function(){
16335                           el.sync();  
16336                       }
16337                   });
16338                   dd.setHandleElId(tipTitle.id);
16339                   dd.lock();
16340               }
16341               inited = true;
16342           }
16343           this.enable(); 
16344        },
16345
16346     /**
16347      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16348      * are supported:
16349      * <pre>
16350 Property    Type                   Description
16351 ----------  ---------------------  ------------------------------------------------------------------------
16352 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16353      * </ul>
16354      * @param {Object} config The config object
16355      */
16356        register : function(config){
16357            var cs = config instanceof Array ? config : arguments;
16358            for(var i = 0, len = cs.length; i < len; i++) {
16359                var c = cs[i];
16360                var target = c.target;
16361                if(target){
16362                    if(target instanceof Array){
16363                        for(var j = 0, jlen = target.length; j < jlen; j++){
16364                            tagEls[target[j]] = c;
16365                        }
16366                    }else{
16367                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16368                    }
16369                }
16370            }
16371        },
16372
16373     /**
16374      * Removes this quick tip from its element and destroys it.
16375      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16376      */
16377        unregister : function(el){
16378            delete tagEls[Roo.id(el)];
16379        },
16380
16381     /**
16382      * Enable this quick tip.
16383      */
16384        enable : function(){
16385            if(inited && disabled){
16386                locks.pop();
16387                if(locks.length < 1){
16388                    disabled = false;
16389                }
16390            }
16391        },
16392
16393     /**
16394      * Disable this quick tip.
16395      */
16396        disable : function(){
16397           disabled = true;
16398           clearTimeout(showProc);
16399           clearTimeout(hideProc);
16400           clearTimeout(dismissProc);
16401           if(ce){
16402               hide(true);
16403           }
16404           locks.push(1);
16405        },
16406
16407     /**
16408      * Returns true if the quick tip is enabled, else false.
16409      */
16410        isEnabled : function(){
16411             return !disabled;
16412        },
16413
16414         // private
16415        tagConfig : {
16416            namespace : "ext",
16417            attribute : "qtip",
16418            width : "width",
16419            target : "target",
16420            title : "qtitle",
16421            hide : "hide",
16422            cls : "qclass"
16423        }
16424    };
16425 }();
16426
16427 // backwards compat
16428 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16429  * Based on:
16430  * Ext JS Library 1.1.1
16431  * Copyright(c) 2006-2007, Ext JS, LLC.
16432  *
16433  * Originally Released Under LGPL - original licence link has changed is not relivant.
16434  *
16435  * Fork - LGPL
16436  * <script type="text/javascript">
16437  */
16438  
16439
16440 /**
16441  * @class Roo.tree.TreePanel
16442  * @extends Roo.data.Tree
16443
16444  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16445  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16446  * @cfg {Boolean} enableDD true to enable drag and drop
16447  * @cfg {Boolean} enableDrag true to enable just drag
16448  * @cfg {Boolean} enableDrop true to enable just drop
16449  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16450  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16451  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16452  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16453  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16454  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16455  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16456  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16457  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16458  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16459  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16460  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16461  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
16462  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16463  * @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>
16464  * @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>
16465  * 
16466  * @constructor
16467  * @param {String/HTMLElement/Element} el The container element
16468  * @param {Object} config
16469  */
16470 Roo.tree.TreePanel = function(el, config){
16471     var root = false;
16472     var loader = false;
16473     if (config.root) {
16474         root = config.root;
16475         delete config.root;
16476     }
16477     if (config.loader) {
16478         loader = config.loader;
16479         delete config.loader;
16480     }
16481     
16482     Roo.apply(this, config);
16483     Roo.tree.TreePanel.superclass.constructor.call(this);
16484     this.el = Roo.get(el);
16485     this.el.addClass('x-tree');
16486     //console.log(root);
16487     if (root) {
16488         this.setRootNode( Roo.factory(root, Roo.tree));
16489     }
16490     if (loader) {
16491         this.loader = Roo.factory(loader, Roo.tree);
16492     }
16493    /**
16494     * Read-only. The id of the container element becomes this TreePanel's id.
16495     */
16496     this.id = this.el.id;
16497     this.addEvents({
16498         /**
16499         * @event beforeload
16500         * Fires before a node is loaded, return false to cancel
16501         * @param {Node} node The node being loaded
16502         */
16503         "beforeload" : true,
16504         /**
16505         * @event load
16506         * Fires when a node is loaded
16507         * @param {Node} node The node that was loaded
16508         */
16509         "load" : true,
16510         /**
16511         * @event textchange
16512         * Fires when the text for a node is changed
16513         * @param {Node} node The node
16514         * @param {String} text The new text
16515         * @param {String} oldText The old text
16516         */
16517         "textchange" : true,
16518         /**
16519         * @event beforeexpand
16520         * Fires before a node is expanded, return false to cancel.
16521         * @param {Node} node The node
16522         * @param {Boolean} deep
16523         * @param {Boolean} anim
16524         */
16525         "beforeexpand" : true,
16526         /**
16527         * @event beforecollapse
16528         * Fires before a node is collapsed, return false to cancel.
16529         * @param {Node} node The node
16530         * @param {Boolean} deep
16531         * @param {Boolean} anim
16532         */
16533         "beforecollapse" : true,
16534         /**
16535         * @event expand
16536         * Fires when a node is expanded
16537         * @param {Node} node The node
16538         */
16539         "expand" : true,
16540         /**
16541         * @event disabledchange
16542         * Fires when the disabled status of a node changes
16543         * @param {Node} node The node
16544         * @param {Boolean} disabled
16545         */
16546         "disabledchange" : true,
16547         /**
16548         * @event collapse
16549         * Fires when a node is collapsed
16550         * @param {Node} node The node
16551         */
16552         "collapse" : true,
16553         /**
16554         * @event beforeclick
16555         * Fires before click processing on a node. Return false to cancel the default action.
16556         * @param {Node} node The node
16557         * @param {Roo.EventObject} e The event object
16558         */
16559         "beforeclick":true,
16560         /**
16561         * @event checkchange
16562         * Fires when a node with a checkbox's checked property changes
16563         * @param {Node} this This node
16564         * @param {Boolean} checked
16565         */
16566         "checkchange":true,
16567         /**
16568         * @event click
16569         * Fires when a node is clicked
16570         * @param {Node} node The node
16571         * @param {Roo.EventObject} e The event object
16572         */
16573         "click":true,
16574         /**
16575         * @event dblclick
16576         * Fires when a node is double clicked
16577         * @param {Node} node The node
16578         * @param {Roo.EventObject} e The event object
16579         */
16580         "dblclick":true,
16581         /**
16582         * @event contextmenu
16583         * Fires when a node is right clicked
16584         * @param {Node} node The node
16585         * @param {Roo.EventObject} e The event object
16586         */
16587         "contextmenu":true,
16588         /**
16589         * @event beforechildrenrendered
16590         * Fires right before the child nodes for a node are rendered
16591         * @param {Node} node The node
16592         */
16593         "beforechildrenrendered":true,
16594         /**
16595         * @event startdrag
16596         * Fires when a node starts being dragged
16597         * @param {Roo.tree.TreePanel} this
16598         * @param {Roo.tree.TreeNode} node
16599         * @param {event} e The raw browser event
16600         */ 
16601        "startdrag" : true,
16602        /**
16603         * @event enddrag
16604         * Fires when a drag operation is complete
16605         * @param {Roo.tree.TreePanel} this
16606         * @param {Roo.tree.TreeNode} node
16607         * @param {event} e The raw browser event
16608         */
16609        "enddrag" : true,
16610        /**
16611         * @event dragdrop
16612         * Fires when a dragged node is dropped on a valid DD target
16613         * @param {Roo.tree.TreePanel} this
16614         * @param {Roo.tree.TreeNode} node
16615         * @param {DD} dd The dd it was dropped on
16616         * @param {event} e The raw browser event
16617         */
16618        "dragdrop" : true,
16619        /**
16620         * @event beforenodedrop
16621         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16622         * passed to handlers has the following properties:<br />
16623         * <ul style="padding:5px;padding-left:16px;">
16624         * <li>tree - The TreePanel</li>
16625         * <li>target - The node being targeted for the drop</li>
16626         * <li>data - The drag data from the drag source</li>
16627         * <li>point - The point of the drop - append, above or below</li>
16628         * <li>source - The drag source</li>
16629         * <li>rawEvent - Raw mouse event</li>
16630         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16631         * to be inserted by setting them on this object.</li>
16632         * <li>cancel - Set this to true to cancel the drop.</li>
16633         * </ul>
16634         * @param {Object} dropEvent
16635         */
16636        "beforenodedrop" : true,
16637        /**
16638         * @event nodedrop
16639         * Fires after a DD object is dropped on a node in this tree. The dropEvent
16640         * passed to handlers has the following properties:<br />
16641         * <ul style="padding:5px;padding-left:16px;">
16642         * <li>tree - The TreePanel</li>
16643         * <li>target - The node being targeted for the drop</li>
16644         * <li>data - The drag data from the drag source</li>
16645         * <li>point - The point of the drop - append, above or below</li>
16646         * <li>source - The drag source</li>
16647         * <li>rawEvent - Raw mouse event</li>
16648         * <li>dropNode - Dropped node(s).</li>
16649         * </ul>
16650         * @param {Object} dropEvent
16651         */
16652        "nodedrop" : true,
16653         /**
16654         * @event nodedragover
16655         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16656         * passed to handlers has the following properties:<br />
16657         * <ul style="padding:5px;padding-left:16px;">
16658         * <li>tree - The TreePanel</li>
16659         * <li>target - The node being targeted for the drop</li>
16660         * <li>data - The drag data from the drag source</li>
16661         * <li>point - The point of the drop - append, above or below</li>
16662         * <li>source - The drag source</li>
16663         * <li>rawEvent - Raw mouse event</li>
16664         * <li>dropNode - Drop node(s) provided by the source.</li>
16665         * <li>cancel - Set this to true to signal drop not allowed.</li>
16666         * </ul>
16667         * @param {Object} dragOverEvent
16668         */
16669        "nodedragover" : true
16670         
16671     });
16672     if(this.singleExpand){
16673        this.on("beforeexpand", this.restrictExpand, this);
16674     }
16675     if (this.editor) {
16676         this.editor.tree = this;
16677         this.editor = Roo.factory(this.editor, Roo.tree);
16678     }
16679     
16680     if (this.selModel) {
16681         this.selModel = Roo.factory(this.selModel, Roo.tree);
16682     }
16683    
16684 };
16685 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16686     rootVisible : true,
16687     animate: Roo.enableFx,
16688     lines : true,
16689     enableDD : false,
16690     hlDrop : Roo.enableFx,
16691   
16692     renderer: false,
16693     
16694     rendererTip: false,
16695     // private
16696     restrictExpand : function(node){
16697         var p = node.parentNode;
16698         if(p){
16699             if(p.expandedChild && p.expandedChild.parentNode == p){
16700                 p.expandedChild.collapse();
16701             }
16702             p.expandedChild = node;
16703         }
16704     },
16705
16706     // private override
16707     setRootNode : function(node){
16708         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16709         if(!this.rootVisible){
16710             node.ui = new Roo.tree.RootTreeNodeUI(node);
16711         }
16712         return node;
16713     },
16714
16715     /**
16716      * Returns the container element for this TreePanel
16717      */
16718     getEl : function(){
16719         return this.el;
16720     },
16721
16722     /**
16723      * Returns the default TreeLoader for this TreePanel
16724      */
16725     getLoader : function(){
16726         return this.loader;
16727     },
16728
16729     /**
16730      * Expand all nodes
16731      */
16732     expandAll : function(){
16733         this.root.expand(true);
16734     },
16735
16736     /**
16737      * Collapse all nodes
16738      */
16739     collapseAll : function(){
16740         this.root.collapse(true);
16741     },
16742
16743     /**
16744      * Returns the selection model used by this TreePanel
16745      */
16746     getSelectionModel : function(){
16747         if(!this.selModel){
16748             this.selModel = new Roo.tree.DefaultSelectionModel();
16749         }
16750         return this.selModel;
16751     },
16752
16753     /**
16754      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16755      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16756      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16757      * @return {Array}
16758      */
16759     getChecked : function(a, startNode){
16760         startNode = startNode || this.root;
16761         var r = [];
16762         var f = function(){
16763             if(this.attributes.checked){
16764                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16765             }
16766         }
16767         startNode.cascade(f);
16768         return r;
16769     },
16770
16771     /**
16772      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16773      * @param {String} path
16774      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16775      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16776      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16777      */
16778     expandPath : function(path, attr, callback){
16779         attr = attr || "id";
16780         var keys = path.split(this.pathSeparator);
16781         var curNode = this.root;
16782         if(curNode.attributes[attr] != keys[1]){ // invalid root
16783             if(callback){
16784                 callback(false, null);
16785             }
16786             return;
16787         }
16788         var index = 1;
16789         var f = function(){
16790             if(++index == keys.length){
16791                 if(callback){
16792                     callback(true, curNode);
16793                 }
16794                 return;
16795             }
16796             var c = curNode.findChild(attr, keys[index]);
16797             if(!c){
16798                 if(callback){
16799                     callback(false, curNode);
16800                 }
16801                 return;
16802             }
16803             curNode = c;
16804             c.expand(false, false, f);
16805         };
16806         curNode.expand(false, false, f);
16807     },
16808
16809     /**
16810      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16811      * @param {String} path
16812      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16813      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16814      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16815      */
16816     selectPath : function(path, attr, callback){
16817         attr = attr || "id";
16818         var keys = path.split(this.pathSeparator);
16819         var v = keys.pop();
16820         if(keys.length > 0){
16821             var f = function(success, node){
16822                 if(success && node){
16823                     var n = node.findChild(attr, v);
16824                     if(n){
16825                         n.select();
16826                         if(callback){
16827                             callback(true, n);
16828                         }
16829                     }else if(callback){
16830                         callback(false, n);
16831                     }
16832                 }else{
16833                     if(callback){
16834                         callback(false, n);
16835                     }
16836                 }
16837             };
16838             this.expandPath(keys.join(this.pathSeparator), attr, f);
16839         }else{
16840             this.root.select();
16841             if(callback){
16842                 callback(true, this.root);
16843             }
16844         }
16845     },
16846
16847     getTreeEl : function(){
16848         return this.el;
16849     },
16850
16851     /**
16852      * Trigger rendering of this TreePanel
16853      */
16854     render : function(){
16855         if (this.innerCt) {
16856             return this; // stop it rendering more than once!!
16857         }
16858         
16859         this.innerCt = this.el.createChild({tag:"ul",
16860                cls:"x-tree-root-ct " +
16861                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16862
16863         if(this.containerScroll){
16864             Roo.dd.ScrollManager.register(this.el);
16865         }
16866         if((this.enableDD || this.enableDrop) && !this.dropZone){
16867            /**
16868             * The dropZone used by this tree if drop is enabled
16869             * @type Roo.tree.TreeDropZone
16870             */
16871              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16872                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16873            });
16874         }
16875         if((this.enableDD || this.enableDrag) && !this.dragZone){
16876            /**
16877             * The dragZone used by this tree if drag is enabled
16878             * @type Roo.tree.TreeDragZone
16879             */
16880             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16881                ddGroup: this.ddGroup || "TreeDD",
16882                scroll: this.ddScroll
16883            });
16884         }
16885         this.getSelectionModel().init(this);
16886         if (!this.root) {
16887             console.log("ROOT not set in tree");
16888             return;
16889         }
16890         this.root.render();
16891         if(!this.rootVisible){
16892             this.root.renderChildren();
16893         }
16894         return this;
16895     }
16896 });/*
16897  * Based on:
16898  * Ext JS Library 1.1.1
16899  * Copyright(c) 2006-2007, Ext JS, LLC.
16900  *
16901  * Originally Released Under LGPL - original licence link has changed is not relivant.
16902  *
16903  * Fork - LGPL
16904  * <script type="text/javascript">
16905  */
16906  
16907
16908 /**
16909  * @class Roo.tree.DefaultSelectionModel
16910  * @extends Roo.util.Observable
16911  * The default single selection for a TreePanel.
16912  * @param {Object} cfg Configuration
16913  */
16914 Roo.tree.DefaultSelectionModel = function(cfg){
16915    this.selNode = null;
16916    
16917    
16918    
16919    this.addEvents({
16920        /**
16921         * @event selectionchange
16922         * Fires when the selected node changes
16923         * @param {DefaultSelectionModel} this
16924         * @param {TreeNode} node the new selection
16925         */
16926        "selectionchange" : true,
16927
16928        /**
16929         * @event beforeselect
16930         * Fires before the selected node changes, return false to cancel the change
16931         * @param {DefaultSelectionModel} this
16932         * @param {TreeNode} node the new selection
16933         * @param {TreeNode} node the old selection
16934         */
16935        "beforeselect" : true
16936    });
16937    
16938     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
16939 };
16940
16941 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16942     init : function(tree){
16943         this.tree = tree;
16944         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16945         tree.on("click", this.onNodeClick, this);
16946     },
16947     
16948     onNodeClick : function(node, e){
16949         if (e.ctrlKey && this.selNode == node)  {
16950             this.unselect(node);
16951             return;
16952         }
16953         this.select(node);
16954     },
16955     
16956     /**
16957      * Select a node.
16958      * @param {TreeNode} node The node to select
16959      * @return {TreeNode} The selected node
16960      */
16961     select : function(node){
16962         var last = this.selNode;
16963         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16964             if(last){
16965                 last.ui.onSelectedChange(false);
16966             }
16967             this.selNode = node;
16968             node.ui.onSelectedChange(true);
16969             this.fireEvent("selectionchange", this, node, last);
16970         }
16971         return node;
16972     },
16973     
16974     /**
16975      * Deselect a node.
16976      * @param {TreeNode} node The node to unselect
16977      */
16978     unselect : function(node){
16979         if(this.selNode == node){
16980             this.clearSelections();
16981         }    
16982     },
16983     
16984     /**
16985      * Clear all selections
16986      */
16987     clearSelections : function(){
16988         var n = this.selNode;
16989         if(n){
16990             n.ui.onSelectedChange(false);
16991             this.selNode = null;
16992             this.fireEvent("selectionchange", this, null);
16993         }
16994         return n;
16995     },
16996     
16997     /**
16998      * Get the selected node
16999      * @return {TreeNode} The selected node
17000      */
17001     getSelectedNode : function(){
17002         return this.selNode;    
17003     },
17004     
17005     /**
17006      * Returns true if the node is selected
17007      * @param {TreeNode} node The node to check
17008      * @return {Boolean}
17009      */
17010     isSelected : function(node){
17011         return this.selNode == node;  
17012     },
17013
17014     /**
17015      * Selects the node above the selected node in the tree, intelligently walking the nodes
17016      * @return TreeNode The new selection
17017      */
17018     selectPrevious : function(){
17019         var s = this.selNode || this.lastSelNode;
17020         if(!s){
17021             return null;
17022         }
17023         var ps = s.previousSibling;
17024         if(ps){
17025             if(!ps.isExpanded() || ps.childNodes.length < 1){
17026                 return this.select(ps);
17027             } else{
17028                 var lc = ps.lastChild;
17029                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
17030                     lc = lc.lastChild;
17031                 }
17032                 return this.select(lc);
17033             }
17034         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
17035             return this.select(s.parentNode);
17036         }
17037         return null;
17038     },
17039
17040     /**
17041      * Selects the node above the selected node in the tree, intelligently walking the nodes
17042      * @return TreeNode The new selection
17043      */
17044     selectNext : function(){
17045         var s = this.selNode || this.lastSelNode;
17046         if(!s){
17047             return null;
17048         }
17049         if(s.firstChild && s.isExpanded()){
17050              return this.select(s.firstChild);
17051          }else if(s.nextSibling){
17052              return this.select(s.nextSibling);
17053          }else if(s.parentNode){
17054             var newS = null;
17055             s.parentNode.bubble(function(){
17056                 if(this.nextSibling){
17057                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
17058                     return false;
17059                 }
17060             });
17061             return newS;
17062          }
17063         return null;
17064     },
17065
17066     onKeyDown : function(e){
17067         var s = this.selNode || this.lastSelNode;
17068         // undesirable, but required
17069         var sm = this;
17070         if(!s){
17071             return;
17072         }
17073         var k = e.getKey();
17074         switch(k){
17075              case e.DOWN:
17076                  e.stopEvent();
17077                  this.selectNext();
17078              break;
17079              case e.UP:
17080                  e.stopEvent();
17081                  this.selectPrevious();
17082              break;
17083              case e.RIGHT:
17084                  e.preventDefault();
17085                  if(s.hasChildNodes()){
17086                      if(!s.isExpanded()){
17087                          s.expand();
17088                      }else if(s.firstChild){
17089                          this.select(s.firstChild, e);
17090                      }
17091                  }
17092              break;
17093              case e.LEFT:
17094                  e.preventDefault();
17095                  if(s.hasChildNodes() && s.isExpanded()){
17096                      s.collapse();
17097                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
17098                      this.select(s.parentNode, e);
17099                  }
17100              break;
17101         };
17102     }
17103 });
17104
17105 /**
17106  * @class Roo.tree.MultiSelectionModel
17107  * @extends Roo.util.Observable
17108  * Multi selection for a TreePanel.
17109  * @param {Object} cfg Configuration
17110  */
17111 Roo.tree.MultiSelectionModel = function(){
17112    this.selNodes = [];
17113    this.selMap = {};
17114    this.addEvents({
17115        /**
17116         * @event selectionchange
17117         * Fires when the selected nodes change
17118         * @param {MultiSelectionModel} this
17119         * @param {Array} nodes Array of the selected nodes
17120         */
17121        "selectionchange" : true
17122    });
17123    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
17124    
17125 };
17126
17127 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
17128     init : function(tree){
17129         this.tree = tree;
17130         tree.getTreeEl().on("keydown", this.onKeyDown, this);
17131         tree.on("click", this.onNodeClick, this);
17132     },
17133     
17134     onNodeClick : function(node, e){
17135         this.select(node, e, e.ctrlKey);
17136     },
17137     
17138     /**
17139      * Select a node.
17140      * @param {TreeNode} node The node to select
17141      * @param {EventObject} e (optional) An event associated with the selection
17142      * @param {Boolean} keepExisting True to retain existing selections
17143      * @return {TreeNode} The selected node
17144      */
17145     select : function(node, e, keepExisting){
17146         if(keepExisting !== true){
17147             this.clearSelections(true);
17148         }
17149         if(this.isSelected(node)){
17150             this.lastSelNode = node;
17151             return node;
17152         }
17153         this.selNodes.push(node);
17154         this.selMap[node.id] = node;
17155         this.lastSelNode = node;
17156         node.ui.onSelectedChange(true);
17157         this.fireEvent("selectionchange", this, this.selNodes);
17158         return node;
17159     },
17160     
17161     /**
17162      * Deselect a node.
17163      * @param {TreeNode} node The node to unselect
17164      */
17165     unselect : function(node){
17166         if(this.selMap[node.id]){
17167             node.ui.onSelectedChange(false);
17168             var sn = this.selNodes;
17169             var index = -1;
17170             if(sn.indexOf){
17171                 index = sn.indexOf(node);
17172             }else{
17173                 for(var i = 0, len = sn.length; i < len; i++){
17174                     if(sn[i] == node){
17175                         index = i;
17176                         break;
17177                     }
17178                 }
17179             }
17180             if(index != -1){
17181                 this.selNodes.splice(index, 1);
17182             }
17183             delete this.selMap[node.id];
17184             this.fireEvent("selectionchange", this, this.selNodes);
17185         }
17186     },
17187     
17188     /**
17189      * Clear all selections
17190      */
17191     clearSelections : function(suppressEvent){
17192         var sn = this.selNodes;
17193         if(sn.length > 0){
17194             for(var i = 0, len = sn.length; i < len; i++){
17195                 sn[i].ui.onSelectedChange(false);
17196             }
17197             this.selNodes = [];
17198             this.selMap = {};
17199             if(suppressEvent !== true){
17200                 this.fireEvent("selectionchange", this, this.selNodes);
17201             }
17202         }
17203     },
17204     
17205     /**
17206      * Returns true if the node is selected
17207      * @param {TreeNode} node The node to check
17208      * @return {Boolean}
17209      */
17210     isSelected : function(node){
17211         return this.selMap[node.id] ? true : false;  
17212     },
17213     
17214     /**
17215      * Returns an array of the selected nodes
17216      * @return {Array}
17217      */
17218     getSelectedNodes : function(){
17219         return this.selNodes;    
17220     },
17221
17222     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17223
17224     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17225
17226     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17227 });/*
17228  * Based on:
17229  * Ext JS Library 1.1.1
17230  * Copyright(c) 2006-2007, Ext JS, LLC.
17231  *
17232  * Originally Released Under LGPL - original licence link has changed is not relivant.
17233  *
17234  * Fork - LGPL
17235  * <script type="text/javascript">
17236  */
17237  
17238 /**
17239  * @class Roo.tree.TreeNode
17240  * @extends Roo.data.Node
17241  * @cfg {String} text The text for this node
17242  * @cfg {Boolean} expanded true to start the node expanded
17243  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17244  * @cfg {Boolean} allowDrop false if this node cannot be drop on
17245  * @cfg {Boolean} disabled true to start the node disabled
17246  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17247  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17248  * @cfg {String} cls A css class to be added to the node
17249  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17250  * @cfg {String} href URL of the link used for the node (defaults to #)
17251  * @cfg {String} hrefTarget target frame for the link
17252  * @cfg {String} qtip An Ext QuickTip for the node
17253  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17254  * @cfg {Boolean} singleClickExpand True for single click expand on this node
17255  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17256  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17257  * (defaults to undefined with no checkbox rendered)
17258  * @constructor
17259  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17260  */
17261 Roo.tree.TreeNode = function(attributes){
17262     attributes = attributes || {};
17263     if(typeof attributes == "string"){
17264         attributes = {text: attributes};
17265     }
17266     this.childrenRendered = false;
17267     this.rendered = false;
17268     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17269     this.expanded = attributes.expanded === true;
17270     this.isTarget = attributes.isTarget !== false;
17271     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17272     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17273
17274     /**
17275      * Read-only. The text for this node. To change it use setText().
17276      * @type String
17277      */
17278     this.text = attributes.text;
17279     /**
17280      * True if this node is disabled.
17281      * @type Boolean
17282      */
17283     this.disabled = attributes.disabled === true;
17284
17285     this.addEvents({
17286         /**
17287         * @event textchange
17288         * Fires when the text for this node is changed
17289         * @param {Node} this This node
17290         * @param {String} text The new text
17291         * @param {String} oldText The old text
17292         */
17293         "textchange" : true,
17294         /**
17295         * @event beforeexpand
17296         * Fires before this node is expanded, return false to cancel.
17297         * @param {Node} this This node
17298         * @param {Boolean} deep
17299         * @param {Boolean} anim
17300         */
17301         "beforeexpand" : true,
17302         /**
17303         * @event beforecollapse
17304         * Fires before this node is collapsed, return false to cancel.
17305         * @param {Node} this This node
17306         * @param {Boolean} deep
17307         * @param {Boolean} anim
17308         */
17309         "beforecollapse" : true,
17310         /**
17311         * @event expand
17312         * Fires when this node is expanded
17313         * @param {Node} this This node
17314         */
17315         "expand" : true,
17316         /**
17317         * @event disabledchange
17318         * Fires when the disabled status of this node changes
17319         * @param {Node} this This node
17320         * @param {Boolean} disabled
17321         */
17322         "disabledchange" : true,
17323         /**
17324         * @event collapse
17325         * Fires when this node is collapsed
17326         * @param {Node} this This node
17327         */
17328         "collapse" : true,
17329         /**
17330         * @event beforeclick
17331         * Fires before click processing. Return false to cancel the default action.
17332         * @param {Node} this This node
17333         * @param {Roo.EventObject} e The event object
17334         */
17335         "beforeclick":true,
17336         /**
17337         * @event checkchange
17338         * Fires when a node with a checkbox's checked property changes
17339         * @param {Node} this This node
17340         * @param {Boolean} checked
17341         */
17342         "checkchange":true,
17343         /**
17344         * @event click
17345         * Fires when this node is clicked
17346         * @param {Node} this This node
17347         * @param {Roo.EventObject} e The event object
17348         */
17349         "click":true,
17350         /**
17351         * @event dblclick
17352         * Fires when this node is double clicked
17353         * @param {Node} this This node
17354         * @param {Roo.EventObject} e The event object
17355         */
17356         "dblclick":true,
17357         /**
17358         * @event contextmenu
17359         * Fires when this node is right clicked
17360         * @param {Node} this This node
17361         * @param {Roo.EventObject} e The event object
17362         */
17363         "contextmenu":true,
17364         /**
17365         * @event beforechildrenrendered
17366         * Fires right before the child nodes for this node are rendered
17367         * @param {Node} this This node
17368         */
17369         "beforechildrenrendered":true
17370     });
17371
17372     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17373
17374     /**
17375      * Read-only. The UI for this node
17376      * @type TreeNodeUI
17377      */
17378     this.ui = new uiClass(this);
17379 };
17380 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17381     preventHScroll: true,
17382     /**
17383      * Returns true if this node is expanded
17384      * @return {Boolean}
17385      */
17386     isExpanded : function(){
17387         return this.expanded;
17388     },
17389
17390     /**
17391      * Returns the UI object for this node
17392      * @return {TreeNodeUI}
17393      */
17394     getUI : function(){
17395         return this.ui;
17396     },
17397
17398     // private override
17399     setFirstChild : function(node){
17400         var of = this.firstChild;
17401         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17402         if(this.childrenRendered && of && node != of){
17403             of.renderIndent(true, true);
17404         }
17405         if(this.rendered){
17406             this.renderIndent(true, true);
17407         }
17408     },
17409
17410     // private override
17411     setLastChild : function(node){
17412         var ol = this.lastChild;
17413         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17414         if(this.childrenRendered && ol && node != ol){
17415             ol.renderIndent(true, true);
17416         }
17417         if(this.rendered){
17418             this.renderIndent(true, true);
17419         }
17420     },
17421
17422     // these methods are overridden to provide lazy rendering support
17423     // private override
17424     appendChild : function(){
17425         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17426         if(node && this.childrenRendered){
17427             node.render();
17428         }
17429         this.ui.updateExpandIcon();
17430         return node;
17431     },
17432
17433     // private override
17434     removeChild : function(node){
17435         this.ownerTree.getSelectionModel().unselect(node);
17436         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17437         // if it's been rendered remove dom node
17438         if(this.childrenRendered){
17439             node.ui.remove();
17440         }
17441         if(this.childNodes.length < 1){
17442             this.collapse(false, false);
17443         }else{
17444             this.ui.updateExpandIcon();
17445         }
17446         if(!this.firstChild) {
17447             this.childrenRendered = false;
17448         }
17449         return node;
17450     },
17451
17452     // private override
17453     insertBefore : function(node, refNode){
17454         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17455         if(newNode && refNode && this.childrenRendered){
17456             node.render();
17457         }
17458         this.ui.updateExpandIcon();
17459         return newNode;
17460     },
17461
17462     /**
17463      * Sets the text for this node
17464      * @param {String} text
17465      */
17466     setText : function(text){
17467         var oldText = this.text;
17468         this.text = text;
17469         this.attributes.text = text;
17470         if(this.rendered){ // event without subscribing
17471             this.ui.onTextChange(this, text, oldText);
17472         }
17473         this.fireEvent("textchange", this, text, oldText);
17474     },
17475
17476     /**
17477      * Triggers selection of this node
17478      */
17479     select : function(){
17480         this.getOwnerTree().getSelectionModel().select(this);
17481     },
17482
17483     /**
17484      * Triggers deselection of this node
17485      */
17486     unselect : function(){
17487         this.getOwnerTree().getSelectionModel().unselect(this);
17488     },
17489
17490     /**
17491      * Returns true if this node is selected
17492      * @return {Boolean}
17493      */
17494     isSelected : function(){
17495         return this.getOwnerTree().getSelectionModel().isSelected(this);
17496     },
17497
17498     /**
17499      * Expand this node.
17500      * @param {Boolean} deep (optional) True to expand all children as well
17501      * @param {Boolean} anim (optional) false to cancel the default animation
17502      * @param {Function} callback (optional) A callback to be called when
17503      * expanding this node completes (does not wait for deep expand to complete).
17504      * Called with 1 parameter, this node.
17505      */
17506     expand : function(deep, anim, callback){
17507         if(!this.expanded){
17508             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17509                 return;
17510             }
17511             if(!this.childrenRendered){
17512                 this.renderChildren();
17513             }
17514             this.expanded = true;
17515             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17516                 this.ui.animExpand(function(){
17517                     this.fireEvent("expand", this);
17518                     if(typeof callback == "function"){
17519                         callback(this);
17520                     }
17521                     if(deep === true){
17522                         this.expandChildNodes(true);
17523                     }
17524                 }.createDelegate(this));
17525                 return;
17526             }else{
17527                 this.ui.expand();
17528                 this.fireEvent("expand", this);
17529                 if(typeof callback == "function"){
17530                     callback(this);
17531                 }
17532             }
17533         }else{
17534            if(typeof callback == "function"){
17535                callback(this);
17536            }
17537         }
17538         if(deep === true){
17539             this.expandChildNodes(true);
17540         }
17541     },
17542
17543     isHiddenRoot : function(){
17544         return this.isRoot && !this.getOwnerTree().rootVisible;
17545     },
17546
17547     /**
17548      * Collapse this node.
17549      * @param {Boolean} deep (optional) True to collapse all children as well
17550      * @param {Boolean} anim (optional) false to cancel the default animation
17551      */
17552     collapse : function(deep, anim){
17553         if(this.expanded && !this.isHiddenRoot()){
17554             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17555                 return;
17556             }
17557             this.expanded = false;
17558             if((this.getOwnerTree().animate && anim !== false) || anim){
17559                 this.ui.animCollapse(function(){
17560                     this.fireEvent("collapse", this);
17561                     if(deep === true){
17562                         this.collapseChildNodes(true);
17563                     }
17564                 }.createDelegate(this));
17565                 return;
17566             }else{
17567                 this.ui.collapse();
17568                 this.fireEvent("collapse", this);
17569             }
17570         }
17571         if(deep === true){
17572             var cs = this.childNodes;
17573             for(var i = 0, len = cs.length; i < len; i++) {
17574                 cs[i].collapse(true, false);
17575             }
17576         }
17577     },
17578
17579     // private
17580     delayedExpand : function(delay){
17581         if(!this.expandProcId){
17582             this.expandProcId = this.expand.defer(delay, this);
17583         }
17584     },
17585
17586     // private
17587     cancelExpand : function(){
17588         if(this.expandProcId){
17589             clearTimeout(this.expandProcId);
17590         }
17591         this.expandProcId = false;
17592     },
17593
17594     /**
17595      * Toggles expanded/collapsed state of the node
17596      */
17597     toggle : function(){
17598         if(this.expanded){
17599             this.collapse();
17600         }else{
17601             this.expand();
17602         }
17603     },
17604
17605     /**
17606      * Ensures all parent nodes are expanded
17607      */
17608     ensureVisible : function(callback){
17609         var tree = this.getOwnerTree();
17610         tree.expandPath(this.parentNode.getPath(), false, function(){
17611             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17612             Roo.callback(callback);
17613         }.createDelegate(this));
17614     },
17615
17616     /**
17617      * Expand all child nodes
17618      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17619      */
17620     expandChildNodes : function(deep){
17621         var cs = this.childNodes;
17622         for(var i = 0, len = cs.length; i < len; i++) {
17623                 cs[i].expand(deep);
17624         }
17625     },
17626
17627     /**
17628      * Collapse all child nodes
17629      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17630      */
17631     collapseChildNodes : function(deep){
17632         var cs = this.childNodes;
17633         for(var i = 0, len = cs.length; i < len; i++) {
17634                 cs[i].collapse(deep);
17635         }
17636     },
17637
17638     /**
17639      * Disables this node
17640      */
17641     disable : function(){
17642         this.disabled = true;
17643         this.unselect();
17644         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17645             this.ui.onDisableChange(this, true);
17646         }
17647         this.fireEvent("disabledchange", this, true);
17648     },
17649
17650     /**
17651      * Enables this node
17652      */
17653     enable : function(){
17654         this.disabled = false;
17655         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17656             this.ui.onDisableChange(this, false);
17657         }
17658         this.fireEvent("disabledchange", this, false);
17659     },
17660
17661     // private
17662     renderChildren : function(suppressEvent){
17663         if(suppressEvent !== false){
17664             this.fireEvent("beforechildrenrendered", this);
17665         }
17666         var cs = this.childNodes;
17667         for(var i = 0, len = cs.length; i < len; i++){
17668             cs[i].render(true);
17669         }
17670         this.childrenRendered = true;
17671     },
17672
17673     // private
17674     sort : function(fn, scope){
17675         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17676         if(this.childrenRendered){
17677             var cs = this.childNodes;
17678             for(var i = 0, len = cs.length; i < len; i++){
17679                 cs[i].render(true);
17680             }
17681         }
17682     },
17683
17684     // private
17685     render : function(bulkRender){
17686         this.ui.render(bulkRender);
17687         if(!this.rendered){
17688             this.rendered = true;
17689             if(this.expanded){
17690                 this.expanded = false;
17691                 this.expand(false, false);
17692             }
17693         }
17694     },
17695
17696     // private
17697     renderIndent : function(deep, refresh){
17698         if(refresh){
17699             this.ui.childIndent = null;
17700         }
17701         this.ui.renderIndent();
17702         if(deep === true && this.childrenRendered){
17703             var cs = this.childNodes;
17704             for(var i = 0, len = cs.length; i < len; i++){
17705                 cs[i].renderIndent(true, refresh);
17706             }
17707         }
17708     }
17709 });/*
17710  * Based on:
17711  * Ext JS Library 1.1.1
17712  * Copyright(c) 2006-2007, Ext JS, LLC.
17713  *
17714  * Originally Released Under LGPL - original licence link has changed is not relivant.
17715  *
17716  * Fork - LGPL
17717  * <script type="text/javascript">
17718  */
17719  
17720 /**
17721  * @class Roo.tree.AsyncTreeNode
17722  * @extends Roo.tree.TreeNode
17723  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17724  * @constructor
17725  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17726  */
17727  Roo.tree.AsyncTreeNode = function(config){
17728     this.loaded = false;
17729     this.loading = false;
17730     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17731     /**
17732     * @event beforeload
17733     * Fires before this node is loaded, return false to cancel
17734     * @param {Node} this This node
17735     */
17736     this.addEvents({'beforeload':true, 'load': true});
17737     /**
17738     * @event load
17739     * Fires when this node is loaded
17740     * @param {Node} this This node
17741     */
17742     /**
17743      * The loader used by this node (defaults to using the tree's defined loader)
17744      * @type TreeLoader
17745      * @property loader
17746      */
17747 };
17748 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17749     expand : function(deep, anim, callback){
17750         if(this.loading){ // if an async load is already running, waiting til it's done
17751             var timer;
17752             var f = function(){
17753                 if(!this.loading){ // done loading
17754                     clearInterval(timer);
17755                     this.expand(deep, anim, callback);
17756                 }
17757             }.createDelegate(this);
17758             timer = setInterval(f, 200);
17759             return;
17760         }
17761         if(!this.loaded){
17762             if(this.fireEvent("beforeload", this) === false){
17763                 return;
17764             }
17765             this.loading = true;
17766             this.ui.beforeLoad(this);
17767             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17768             if(loader){
17769                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17770                 return;
17771             }
17772         }
17773         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17774     },
17775     
17776     /**
17777      * Returns true if this node is currently loading
17778      * @return {Boolean}
17779      */
17780     isLoading : function(){
17781         return this.loading;  
17782     },
17783     
17784     loadComplete : function(deep, anim, callback){
17785         this.loading = false;
17786         this.loaded = true;
17787         this.ui.afterLoad(this);
17788         this.fireEvent("load", this);
17789         this.expand(deep, anim, callback);
17790     },
17791     
17792     /**
17793      * Returns true if this node has been loaded
17794      * @return {Boolean}
17795      */
17796     isLoaded : function(){
17797         return this.loaded;
17798     },
17799     
17800     hasChildNodes : function(){
17801         if(!this.isLeaf() && !this.loaded){
17802             return true;
17803         }else{
17804             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17805         }
17806     },
17807
17808     /**
17809      * Trigger a reload for this node
17810      * @param {Function} callback
17811      */
17812     reload : function(callback){
17813         this.collapse(false, false);
17814         while(this.firstChild){
17815             this.removeChild(this.firstChild);
17816         }
17817         this.childrenRendered = false;
17818         this.loaded = false;
17819         if(this.isHiddenRoot()){
17820             this.expanded = false;
17821         }
17822         this.expand(false, false, callback);
17823     }
17824 });/*
17825  * Based on:
17826  * Ext JS Library 1.1.1
17827  * Copyright(c) 2006-2007, Ext JS, LLC.
17828  *
17829  * Originally Released Under LGPL - original licence link has changed is not relivant.
17830  *
17831  * Fork - LGPL
17832  * <script type="text/javascript">
17833  */
17834  
17835 /**
17836  * @class Roo.tree.TreeNodeUI
17837  * @constructor
17838  * @param {Object} node The node to render
17839  * The TreeNode UI implementation is separate from the
17840  * tree implementation. Unless you are customizing the tree UI,
17841  * you should never have to use this directly.
17842  */
17843 Roo.tree.TreeNodeUI = function(node){
17844     this.node = node;
17845     this.rendered = false;
17846     this.animating = false;
17847     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17848 };
17849
17850 Roo.tree.TreeNodeUI.prototype = {
17851     removeChild : function(node){
17852         if(this.rendered){
17853             this.ctNode.removeChild(node.ui.getEl());
17854         }
17855     },
17856
17857     beforeLoad : function(){
17858          this.addClass("x-tree-node-loading");
17859     },
17860
17861     afterLoad : function(){
17862          this.removeClass("x-tree-node-loading");
17863     },
17864
17865     onTextChange : function(node, text, oldText){
17866         if(this.rendered){
17867             this.textNode.innerHTML = text;
17868         }
17869     },
17870
17871     onDisableChange : function(node, state){
17872         this.disabled = state;
17873         if(state){
17874             this.addClass("x-tree-node-disabled");
17875         }else{
17876             this.removeClass("x-tree-node-disabled");
17877         }
17878     },
17879
17880     onSelectedChange : function(state){
17881         if(state){
17882             this.focus();
17883             this.addClass("x-tree-selected");
17884         }else{
17885             //this.blur();
17886             this.removeClass("x-tree-selected");
17887         }
17888     },
17889
17890     onMove : function(tree, node, oldParent, newParent, index, refNode){
17891         this.childIndent = null;
17892         if(this.rendered){
17893             var targetNode = newParent.ui.getContainer();
17894             if(!targetNode){//target not rendered
17895                 this.holder = document.createElement("div");
17896                 this.holder.appendChild(this.wrap);
17897                 return;
17898             }
17899             var insertBefore = refNode ? refNode.ui.getEl() : null;
17900             if(insertBefore){
17901                 targetNode.insertBefore(this.wrap, insertBefore);
17902             }else{
17903                 targetNode.appendChild(this.wrap);
17904             }
17905             this.node.renderIndent(true);
17906         }
17907     },
17908
17909     addClass : function(cls){
17910         if(this.elNode){
17911             Roo.fly(this.elNode).addClass(cls);
17912         }
17913     },
17914
17915     removeClass : function(cls){
17916         if(this.elNode){
17917             Roo.fly(this.elNode).removeClass(cls);
17918         }
17919     },
17920
17921     remove : function(){
17922         if(this.rendered){
17923             this.holder = document.createElement("div");
17924             this.holder.appendChild(this.wrap);
17925         }
17926     },
17927
17928     fireEvent : function(){
17929         return this.node.fireEvent.apply(this.node, arguments);
17930     },
17931
17932     initEvents : function(){
17933         this.node.on("move", this.onMove, this);
17934         var E = Roo.EventManager;
17935         var a = this.anchor;
17936
17937         var el = Roo.fly(a, '_treeui');
17938
17939         if(Roo.isOpera){ // opera render bug ignores the CSS
17940             el.setStyle("text-decoration", "none");
17941         }
17942
17943         el.on("click", this.onClick, this);
17944         el.on("dblclick", this.onDblClick, this);
17945
17946         if(this.checkbox){
17947             Roo.EventManager.on(this.checkbox,
17948                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17949         }
17950
17951         el.on("contextmenu", this.onContextMenu, this);
17952
17953         var icon = Roo.fly(this.iconNode);
17954         icon.on("click", this.onClick, this);
17955         icon.on("dblclick", this.onDblClick, this);
17956         icon.on("contextmenu", this.onContextMenu, this);
17957         E.on(this.ecNode, "click", this.ecClick, this, true);
17958
17959         if(this.node.disabled){
17960             this.addClass("x-tree-node-disabled");
17961         }
17962         if(this.node.hidden){
17963             this.addClass("x-tree-node-disabled");
17964         }
17965         var ot = this.node.getOwnerTree();
17966         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17967         if(dd && (!this.node.isRoot || ot.rootVisible)){
17968             Roo.dd.Registry.register(this.elNode, {
17969                 node: this.node,
17970                 handles: this.getDDHandles(),
17971                 isHandle: false
17972             });
17973         }
17974     },
17975
17976     getDDHandles : function(){
17977         return [this.iconNode, this.textNode];
17978     },
17979
17980     hide : function(){
17981         if(this.rendered){
17982             this.wrap.style.display = "none";
17983         }
17984     },
17985
17986     show : function(){
17987         if(this.rendered){
17988             this.wrap.style.display = "";
17989         }
17990     },
17991
17992     onContextMenu : function(e){
17993         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
17994             e.preventDefault();
17995             this.focus();
17996             this.fireEvent("contextmenu", this.node, e);
17997         }
17998     },
17999
18000     onClick : function(e){
18001         if(this.dropping){
18002             e.stopEvent();
18003             return;
18004         }
18005         if(this.fireEvent("beforeclick", this.node, e) !== false){
18006             if(!this.disabled && this.node.attributes.href){
18007                 this.fireEvent("click", this.node, e);
18008                 return;
18009             }
18010             e.preventDefault();
18011             if(this.disabled){
18012                 return;
18013             }
18014
18015             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
18016                 this.node.toggle();
18017             }
18018
18019             this.fireEvent("click", this.node, e);
18020         }else{
18021             e.stopEvent();
18022         }
18023     },
18024
18025     onDblClick : function(e){
18026         e.preventDefault();
18027         if(this.disabled){
18028             return;
18029         }
18030         if(this.checkbox){
18031             this.toggleCheck();
18032         }
18033         if(!this.animating && this.node.hasChildNodes()){
18034             this.node.toggle();
18035         }
18036         this.fireEvent("dblclick", this.node, e);
18037     },
18038
18039     onCheckChange : function(){
18040         var checked = this.checkbox.checked;
18041         this.node.attributes.checked = checked;
18042         this.fireEvent('checkchange', this.node, checked);
18043     },
18044
18045     ecClick : function(e){
18046         if(!this.animating && this.node.hasChildNodes()){
18047             this.node.toggle();
18048         }
18049     },
18050
18051     startDrop : function(){
18052         this.dropping = true;
18053     },
18054
18055     // delayed drop so the click event doesn't get fired on a drop
18056     endDrop : function(){
18057        setTimeout(function(){
18058            this.dropping = false;
18059        }.createDelegate(this), 50);
18060     },
18061
18062     expand : function(){
18063         this.updateExpandIcon();
18064         this.ctNode.style.display = "";
18065     },
18066
18067     focus : function(){
18068         if(!this.node.preventHScroll){
18069             try{this.anchor.focus();
18070             }catch(e){}
18071         }else if(!Roo.isIE){
18072             try{
18073                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
18074                 var l = noscroll.scrollLeft;
18075                 this.anchor.focus();
18076                 noscroll.scrollLeft = l;
18077             }catch(e){}
18078         }
18079     },
18080
18081     toggleCheck : function(value){
18082         var cb = this.checkbox;
18083         if(cb){
18084             cb.checked = (value === undefined ? !cb.checked : value);
18085         }
18086     },
18087
18088     blur : function(){
18089         try{
18090             this.anchor.blur();
18091         }catch(e){}
18092     },
18093
18094     animExpand : function(callback){
18095         var ct = Roo.get(this.ctNode);
18096         ct.stopFx();
18097         if(!this.node.hasChildNodes()){
18098             this.updateExpandIcon();
18099             this.ctNode.style.display = "";
18100             Roo.callback(callback);
18101             return;
18102         }
18103         this.animating = true;
18104         this.updateExpandIcon();
18105
18106         ct.slideIn('t', {
18107            callback : function(){
18108                this.animating = false;
18109                Roo.callback(callback);
18110             },
18111             scope: this,
18112             duration: this.node.ownerTree.duration || .25
18113         });
18114     },
18115
18116     highlight : function(){
18117         var tree = this.node.getOwnerTree();
18118         Roo.fly(this.wrap).highlight(
18119             tree.hlColor || "C3DAF9",
18120             {endColor: tree.hlBaseColor}
18121         );
18122     },
18123
18124     collapse : function(){
18125         this.updateExpandIcon();
18126         this.ctNode.style.display = "none";
18127     },
18128
18129     animCollapse : function(callback){
18130         var ct = Roo.get(this.ctNode);
18131         ct.enableDisplayMode('block');
18132         ct.stopFx();
18133
18134         this.animating = true;
18135         this.updateExpandIcon();
18136
18137         ct.slideOut('t', {
18138             callback : function(){
18139                this.animating = false;
18140                Roo.callback(callback);
18141             },
18142             scope: this,
18143             duration: this.node.ownerTree.duration || .25
18144         });
18145     },
18146
18147     getContainer : function(){
18148         return this.ctNode;
18149     },
18150
18151     getEl : function(){
18152         return this.wrap;
18153     },
18154
18155     appendDDGhost : function(ghostNode){
18156         ghostNode.appendChild(this.elNode.cloneNode(true));
18157     },
18158
18159     getDDRepairXY : function(){
18160         return Roo.lib.Dom.getXY(this.iconNode);
18161     },
18162
18163     onRender : function(){
18164         this.render();
18165     },
18166
18167     render : function(bulkRender){
18168         var n = this.node, a = n.attributes;
18169         var targetNode = n.parentNode ?
18170               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
18171
18172         if(!this.rendered){
18173             this.rendered = true;
18174
18175             this.renderElements(n, a, targetNode, bulkRender);
18176
18177             if(a.qtip){
18178                if(this.textNode.setAttributeNS){
18179                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
18180                    if(a.qtipTitle){
18181                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
18182                    }
18183                }else{
18184                    this.textNode.setAttribute("ext:qtip", a.qtip);
18185                    if(a.qtipTitle){
18186                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
18187                    }
18188                }
18189             }else if(a.qtipCfg){
18190                 a.qtipCfg.target = Roo.id(this.textNode);
18191                 Roo.QuickTips.register(a.qtipCfg);
18192             }
18193             this.initEvents();
18194             if(!this.node.expanded){
18195                 this.updateExpandIcon();
18196             }
18197         }else{
18198             if(bulkRender === true) {
18199                 targetNode.appendChild(this.wrap);
18200             }
18201         }
18202     },
18203
18204     renderElements : function(n, a, targetNode, bulkRender)
18205     {
18206         // add some indent caching, this helps performance when rendering a large tree
18207         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18208         var t = n.getOwnerTree();
18209         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18210         if (typeof(n.attributes.html) != 'undefined') {
18211             txt = n.attributes.html;
18212         }
18213         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18214         var cb = typeof a.checked == 'boolean';
18215         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18216         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18217             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18218             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18219             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18220             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18221             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18222              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
18223                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18224             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18225             "</li>"];
18226
18227         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18228             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18229                                 n.nextSibling.ui.getEl(), buf.join(""));
18230         }else{
18231             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18232         }
18233
18234         this.elNode = this.wrap.childNodes[0];
18235         this.ctNode = this.wrap.childNodes[1];
18236         var cs = this.elNode.childNodes;
18237         this.indentNode = cs[0];
18238         this.ecNode = cs[1];
18239         this.iconNode = cs[2];
18240         var index = 3;
18241         if(cb){
18242             this.checkbox = cs[3];
18243             index++;
18244         }
18245         this.anchor = cs[index];
18246         this.textNode = cs[index].firstChild;
18247     },
18248
18249     getAnchor : function(){
18250         return this.anchor;
18251     },
18252
18253     getTextEl : function(){
18254         return this.textNode;
18255     },
18256
18257     getIconEl : function(){
18258         return this.iconNode;
18259     },
18260
18261     isChecked : function(){
18262         return this.checkbox ? this.checkbox.checked : false;
18263     },
18264
18265     updateExpandIcon : function(){
18266         if(this.rendered){
18267             var n = this.node, c1, c2;
18268             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18269             var hasChild = n.hasChildNodes();
18270             if(hasChild){
18271                 if(n.expanded){
18272                     cls += "-minus";
18273                     c1 = "x-tree-node-collapsed";
18274                     c2 = "x-tree-node-expanded";
18275                 }else{
18276                     cls += "-plus";
18277                     c1 = "x-tree-node-expanded";
18278                     c2 = "x-tree-node-collapsed";
18279                 }
18280                 if(this.wasLeaf){
18281                     this.removeClass("x-tree-node-leaf");
18282                     this.wasLeaf = false;
18283                 }
18284                 if(this.c1 != c1 || this.c2 != c2){
18285                     Roo.fly(this.elNode).replaceClass(c1, c2);
18286                     this.c1 = c1; this.c2 = c2;
18287                 }
18288             }else{
18289                 // this changes non-leafs into leafs if they have no children.
18290                 // it's not very rational behaviour..
18291                 
18292                 if(!this.wasLeaf && this.node.leaf){
18293                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18294                     delete this.c1;
18295                     delete this.c2;
18296                     this.wasLeaf = true;
18297                 }
18298             }
18299             var ecc = "x-tree-ec-icon "+cls;
18300             if(this.ecc != ecc){
18301                 this.ecNode.className = ecc;
18302                 this.ecc = ecc;
18303             }
18304         }
18305     },
18306
18307     getChildIndent : function(){
18308         if(!this.childIndent){
18309             var buf = [];
18310             var p = this.node;
18311             while(p){
18312                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18313                     if(!p.isLast()) {
18314                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18315                     } else {
18316                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18317                     }
18318                 }
18319                 p = p.parentNode;
18320             }
18321             this.childIndent = buf.join("");
18322         }
18323         return this.childIndent;
18324     },
18325
18326     renderIndent : function(){
18327         if(this.rendered){
18328             var indent = "";
18329             var p = this.node.parentNode;
18330             if(p){
18331                 indent = p.ui.getChildIndent();
18332             }
18333             if(this.indentMarkup != indent){ // don't rerender if not required
18334                 this.indentNode.innerHTML = indent;
18335                 this.indentMarkup = indent;
18336             }
18337             this.updateExpandIcon();
18338         }
18339     }
18340 };
18341
18342 Roo.tree.RootTreeNodeUI = function(){
18343     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18344 };
18345 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18346     render : function(){
18347         if(!this.rendered){
18348             var targetNode = this.node.ownerTree.innerCt.dom;
18349             this.node.expanded = true;
18350             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18351             this.wrap = this.ctNode = targetNode.firstChild;
18352         }
18353     },
18354     collapse : function(){
18355     },
18356     expand : function(){
18357     }
18358 });/*
18359  * Based on:
18360  * Ext JS Library 1.1.1
18361  * Copyright(c) 2006-2007, Ext JS, LLC.
18362  *
18363  * Originally Released Under LGPL - original licence link has changed is not relivant.
18364  *
18365  * Fork - LGPL
18366  * <script type="text/javascript">
18367  */
18368 /**
18369  * @class Roo.tree.TreeLoader
18370  * @extends Roo.util.Observable
18371  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18372  * nodes from a specified URL. The response must be a javascript Array definition
18373  * who's elements are node definition objects. eg:
18374  * <pre><code>
18375    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
18376     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
18377 </code></pre>
18378  * <br><br>
18379  * A server request is sent, and child nodes are loaded only when a node is expanded.
18380  * The loading node's id is passed to the server under the parameter name "node" to
18381  * enable the server to produce the correct child nodes.
18382  * <br><br>
18383  * To pass extra parameters, an event handler may be attached to the "beforeload"
18384  * event, and the parameters specified in the TreeLoader's baseParams property:
18385  * <pre><code>
18386     myTreeLoader.on("beforeload", function(treeLoader, node) {
18387         this.baseParams.category = node.attributes.category;
18388     }, this);
18389 </code></pre><
18390  * This would pass an HTTP parameter called "category" to the server containing
18391  * the value of the Node's "category" attribute.
18392  * @constructor
18393  * Creates a new Treeloader.
18394  * @param {Object} config A config object containing config properties.
18395  */
18396 Roo.tree.TreeLoader = function(config){
18397     this.baseParams = {};
18398     this.requestMethod = "POST";
18399     Roo.apply(this, config);
18400
18401     this.addEvents({
18402     
18403         /**
18404          * @event beforeload
18405          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18406          * @param {Object} This TreeLoader object.
18407          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18408          * @param {Object} callback The callback function specified in the {@link #load} call.
18409          */
18410         beforeload : true,
18411         /**
18412          * @event load
18413          * Fires when the node has been successfuly loaded.
18414          * @param {Object} This TreeLoader object.
18415          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18416          * @param {Object} response The response object containing the data from the server.
18417          */
18418         load : true,
18419         /**
18420          * @event loadexception
18421          * Fires if the network request failed.
18422          * @param {Object} This TreeLoader object.
18423          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18424          * @param {Object} response The response object containing the data from the server.
18425          */
18426         loadexception : true,
18427         /**
18428          * @event create
18429          * Fires before a node is created, enabling you to return custom Node types 
18430          * @param {Object} This TreeLoader object.
18431          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18432          */
18433         create : true
18434     });
18435
18436     Roo.tree.TreeLoader.superclass.constructor.call(this);
18437 };
18438
18439 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18440     /**
18441     * @cfg {String} dataUrl The URL from which to request a Json string which
18442     * specifies an array of node definition object representing the child nodes
18443     * to be loaded.
18444     */
18445     /**
18446     * @cfg {Object} baseParams (optional) An object containing properties which
18447     * specify HTTP parameters to be passed to each request for child nodes.
18448     */
18449     /**
18450     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18451     * created by this loader. If the attributes sent by the server have an attribute in this object,
18452     * they take priority.
18453     */
18454     /**
18455     * @cfg {Object} uiProviders (optional) An object containing properties which
18456     * 
18457     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
18458     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18459     * <i>uiProvider</i> attribute of a returned child node is a string rather
18460     * than a reference to a TreeNodeUI implementation, this that string value
18461     * is used as a property name in the uiProviders object. You can define the provider named
18462     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18463     */
18464     uiProviders : {},
18465
18466     /**
18467     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18468     * child nodes before loading.
18469     */
18470     clearOnLoad : true,
18471
18472     /**
18473     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18474     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18475     * Grid query { data : [ .....] }
18476     */
18477     
18478     root : false,
18479      /**
18480     * @cfg {String} queryParam (optional) 
18481     * Name of the query as it will be passed on the querystring (defaults to 'node')
18482     * eg. the request will be ?node=[id]
18483     */
18484     
18485     
18486     queryParam: false,
18487     
18488     /**
18489      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18490      * This is called automatically when a node is expanded, but may be used to reload
18491      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18492      * @param {Roo.tree.TreeNode} node
18493      * @param {Function} callback
18494      */
18495     load : function(node, callback){
18496         if(this.clearOnLoad){
18497             while(node.firstChild){
18498                 node.removeChild(node.firstChild);
18499             }
18500         }
18501         if(node.attributes.children){ // preloaded json children
18502             var cs = node.attributes.children;
18503             for(var i = 0, len = cs.length; i < len; i++){
18504                 node.appendChild(this.createNode(cs[i]));
18505             }
18506             if(typeof callback == "function"){
18507                 callback();
18508             }
18509         }else if(this.dataUrl){
18510             this.requestData(node, callback);
18511         }
18512     },
18513
18514     getParams: function(node){
18515         var buf = [], bp = this.baseParams;
18516         for(var key in bp){
18517             if(typeof bp[key] != "function"){
18518                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18519             }
18520         }
18521         var n = this.queryParam === false ? 'node' : this.queryParam;
18522         buf.push(n + "=", encodeURIComponent(node.id));
18523         return buf.join("");
18524     },
18525
18526     requestData : function(node, callback){
18527         if(this.fireEvent("beforeload", this, node, callback) !== false){
18528             this.transId = Roo.Ajax.request({
18529                 method:this.requestMethod,
18530                 url: this.dataUrl||this.url,
18531                 success: this.handleResponse,
18532                 failure: this.handleFailure,
18533                 scope: this,
18534                 argument: {callback: callback, node: node},
18535                 params: this.getParams(node)
18536             });
18537         }else{
18538             // if the load is cancelled, make sure we notify
18539             // the node that we are done
18540             if(typeof callback == "function"){
18541                 callback();
18542             }
18543         }
18544     },
18545
18546     isLoading : function(){
18547         return this.transId ? true : false;
18548     },
18549
18550     abort : function(){
18551         if(this.isLoading()){
18552             Roo.Ajax.abort(this.transId);
18553         }
18554     },
18555
18556     // private
18557     createNode : function(attr)
18558     {
18559         // apply baseAttrs, nice idea Corey!
18560         if(this.baseAttrs){
18561             Roo.applyIf(attr, this.baseAttrs);
18562         }
18563         if(this.applyLoader !== false){
18564             attr.loader = this;
18565         }
18566         // uiProvider = depreciated..
18567         
18568         if(typeof(attr.uiProvider) == 'string'){
18569            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18570                 /**  eval:var:attr */ eval(attr.uiProvider);
18571         }
18572         if(typeof(this.uiProviders['default']) != 'undefined') {
18573             attr.uiProvider = this.uiProviders['default'];
18574         }
18575         
18576         this.fireEvent('create', this, attr);
18577         
18578         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18579         return(attr.leaf ?
18580                         new Roo.tree.TreeNode(attr) :
18581                         new Roo.tree.AsyncTreeNode(attr));
18582     },
18583
18584     processResponse : function(response, node, callback)
18585     {
18586         var json = response.responseText;
18587         try {
18588             
18589             var o = Roo.decode(json);
18590             
18591             if (!o.success) {
18592                 // it's a failure condition.
18593                 var a = response.argument;
18594                 this.fireEvent("loadexception", this, a.node, response);
18595                 Roo.log("Load failed - should have a handler really");
18596                 return;
18597             }
18598             
18599             if (this.root !== false) {
18600                 o = o[this.root];
18601             }
18602             
18603             for(var i = 0, len = o.length; i < len; i++){
18604                 var n = this.createNode(o[i]);
18605                 if(n){
18606                     node.appendChild(n);
18607                 }
18608             }
18609             if(typeof callback == "function"){
18610                 callback(this, node);
18611             }
18612         }catch(e){
18613             this.handleFailure(response);
18614         }
18615     },
18616
18617     handleResponse : function(response){
18618         this.transId = false;
18619         var a = response.argument;
18620         this.processResponse(response, a.node, a.callback);
18621         this.fireEvent("load", this, a.node, response);
18622     },
18623
18624     handleFailure : function(response)
18625     {
18626         // should handle failure better..
18627         this.transId = false;
18628         var a = response.argument;
18629         this.fireEvent("loadexception", this, a.node, response);
18630         if(typeof a.callback == "function"){
18631             a.callback(this, a.node);
18632         }
18633     }
18634 });/*
18635  * Based on:
18636  * Ext JS Library 1.1.1
18637  * Copyright(c) 2006-2007, Ext JS, LLC.
18638  *
18639  * Originally Released Under LGPL - original licence link has changed is not relivant.
18640  *
18641  * Fork - LGPL
18642  * <script type="text/javascript">
18643  */
18644
18645 /**
18646 * @class Roo.tree.TreeFilter
18647 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18648 * @param {TreePanel} tree
18649 * @param {Object} config (optional)
18650  */
18651 Roo.tree.TreeFilter = function(tree, config){
18652     this.tree = tree;
18653     this.filtered = {};
18654     Roo.apply(this, config);
18655 };
18656
18657 Roo.tree.TreeFilter.prototype = {
18658     clearBlank:false,
18659     reverse:false,
18660     autoClear:false,
18661     remove:false,
18662
18663      /**
18664      * Filter the data by a specific attribute.
18665      * @param {String/RegExp} value Either string that the attribute value
18666      * should start with or a RegExp to test against the attribute
18667      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18668      * @param {TreeNode} startNode (optional) The node to start the filter at.
18669      */
18670     filter : function(value, attr, startNode){
18671         attr = attr || "text";
18672         var f;
18673         if(typeof value == "string"){
18674             var vlen = value.length;
18675             // auto clear empty filter
18676             if(vlen == 0 && this.clearBlank){
18677                 this.clear();
18678                 return;
18679             }
18680             value = value.toLowerCase();
18681             f = function(n){
18682                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18683             };
18684         }else if(value.exec){ // regex?
18685             f = function(n){
18686                 return value.test(n.attributes[attr]);
18687             };
18688         }else{
18689             throw 'Illegal filter type, must be string or regex';
18690         }
18691         this.filterBy(f, null, startNode);
18692         },
18693
18694     /**
18695      * Filter by a function. The passed function will be called with each
18696      * node in the tree (or from the startNode). If the function returns true, the node is kept
18697      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18698      * @param {Function} fn The filter function
18699      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18700      */
18701     filterBy : function(fn, scope, startNode){
18702         startNode = startNode || this.tree.root;
18703         if(this.autoClear){
18704             this.clear();
18705         }
18706         var af = this.filtered, rv = this.reverse;
18707         var f = function(n){
18708             if(n == startNode){
18709                 return true;
18710             }
18711             if(af[n.id]){
18712                 return false;
18713             }
18714             var m = fn.call(scope || n, n);
18715             if(!m || rv){
18716                 af[n.id] = n;
18717                 n.ui.hide();
18718                 return false;
18719             }
18720             return true;
18721         };
18722         startNode.cascade(f);
18723         if(this.remove){
18724            for(var id in af){
18725                if(typeof id != "function"){
18726                    var n = af[id];
18727                    if(n && n.parentNode){
18728                        n.parentNode.removeChild(n);
18729                    }
18730                }
18731            }
18732         }
18733     },
18734
18735     /**
18736      * Clears the current filter. Note: with the "remove" option
18737      * set a filter cannot be cleared.
18738      */
18739     clear : function(){
18740         var t = this.tree;
18741         var af = this.filtered;
18742         for(var id in af){
18743             if(typeof id != "function"){
18744                 var n = af[id];
18745                 if(n){
18746                     n.ui.show();
18747                 }
18748             }
18749         }
18750         this.filtered = {};
18751     }
18752 };
18753 /*
18754  * Based on:
18755  * Ext JS Library 1.1.1
18756  * Copyright(c) 2006-2007, Ext JS, LLC.
18757  *
18758  * Originally Released Under LGPL - original licence link has changed is not relivant.
18759  *
18760  * Fork - LGPL
18761  * <script type="text/javascript">
18762  */
18763  
18764
18765 /**
18766  * @class Roo.tree.TreeSorter
18767  * Provides sorting of nodes in a TreePanel
18768  * 
18769  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18770  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18771  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18772  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18773  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18774  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18775  * @constructor
18776  * @param {TreePanel} tree
18777  * @param {Object} config
18778  */
18779 Roo.tree.TreeSorter = function(tree, config){
18780     Roo.apply(this, config);
18781     tree.on("beforechildrenrendered", this.doSort, this);
18782     tree.on("append", this.updateSort, this);
18783     tree.on("insert", this.updateSort, this);
18784     
18785     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18786     var p = this.property || "text";
18787     var sortType = this.sortType;
18788     var fs = this.folderSort;
18789     var cs = this.caseSensitive === true;
18790     var leafAttr = this.leafAttr || 'leaf';
18791
18792     this.sortFn = function(n1, n2){
18793         if(fs){
18794             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18795                 return 1;
18796             }
18797             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18798                 return -1;
18799             }
18800         }
18801         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18802         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18803         if(v1 < v2){
18804                         return dsc ? +1 : -1;
18805                 }else if(v1 > v2){
18806                         return dsc ? -1 : +1;
18807         }else{
18808                 return 0;
18809         }
18810     };
18811 };
18812
18813 Roo.tree.TreeSorter.prototype = {
18814     doSort : function(node){
18815         node.sort(this.sortFn);
18816     },
18817     
18818     compareNodes : function(n1, n2){
18819         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18820     },
18821     
18822     updateSort : function(tree, node){
18823         if(node.childrenRendered){
18824             this.doSort.defer(1, this, [node]);
18825         }
18826     }
18827 };/*
18828  * Based on:
18829  * Ext JS Library 1.1.1
18830  * Copyright(c) 2006-2007, Ext JS, LLC.
18831  *
18832  * Originally Released Under LGPL - original licence link has changed is not relivant.
18833  *
18834  * Fork - LGPL
18835  * <script type="text/javascript">
18836  */
18837
18838 if(Roo.dd.DropZone){
18839     
18840 Roo.tree.TreeDropZone = function(tree, config){
18841     this.allowParentInsert = false;
18842     this.allowContainerDrop = false;
18843     this.appendOnly = false;
18844     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18845     this.tree = tree;
18846     this.lastInsertClass = "x-tree-no-status";
18847     this.dragOverData = {};
18848 };
18849
18850 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18851     ddGroup : "TreeDD",
18852     
18853     expandDelay : 1000,
18854     
18855     expandNode : function(node){
18856         if(node.hasChildNodes() && !node.isExpanded()){
18857             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18858         }
18859     },
18860     
18861     queueExpand : function(node){
18862         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18863     },
18864     
18865     cancelExpand : function(){
18866         if(this.expandProcId){
18867             clearTimeout(this.expandProcId);
18868             this.expandProcId = false;
18869         }
18870     },
18871     
18872     isValidDropPoint : function(n, pt, dd, e, data){
18873         if(!n || !data){ return false; }
18874         var targetNode = n.node;
18875         var dropNode = data.node;
18876         // default drop rules
18877         if(!(targetNode && targetNode.isTarget && pt)){
18878             return false;
18879         }
18880         if(pt == "append" && targetNode.allowChildren === false){
18881             return false;
18882         }
18883         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18884             return false;
18885         }
18886         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18887             return false;
18888         }
18889         // reuse the object
18890         var overEvent = this.dragOverData;
18891         overEvent.tree = this.tree;
18892         overEvent.target = targetNode;
18893         overEvent.data = data;
18894         overEvent.point = pt;
18895         overEvent.source = dd;
18896         overEvent.rawEvent = e;
18897         overEvent.dropNode = dropNode;
18898         overEvent.cancel = false;  
18899         var result = this.tree.fireEvent("nodedragover", overEvent);
18900         return overEvent.cancel === false && result !== false;
18901     },
18902     
18903     getDropPoint : function(e, n, dd){
18904         var tn = n.node;
18905         if(tn.isRoot){
18906             return tn.allowChildren !== false ? "append" : false; // always append for root
18907         }
18908         var dragEl = n.ddel;
18909         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18910         var y = Roo.lib.Event.getPageY(e);
18911         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18912         
18913         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18914         var noAppend = tn.allowChildren === false;
18915         if(this.appendOnly || tn.parentNode.allowChildren === false){
18916             return noAppend ? false : "append";
18917         }
18918         var noBelow = false;
18919         if(!this.allowParentInsert){
18920             noBelow = tn.hasChildNodes() && tn.isExpanded();
18921         }
18922         var q = (b - t) / (noAppend ? 2 : 3);
18923         if(y >= t && y < (t + q)){
18924             return "above";
18925         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18926             return "below";
18927         }else{
18928             return "append";
18929         }
18930     },
18931     
18932     onNodeEnter : function(n, dd, e, data){
18933         this.cancelExpand();
18934     },
18935     
18936     onNodeOver : function(n, dd, e, data){
18937         var pt = this.getDropPoint(e, n, dd);
18938         var node = n.node;
18939         
18940         // auto node expand check
18941         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18942             this.queueExpand(node);
18943         }else if(pt != "append"){
18944             this.cancelExpand();
18945         }
18946         
18947         // set the insert point style on the target node
18948         var returnCls = this.dropNotAllowed;
18949         if(this.isValidDropPoint(n, pt, dd, e, data)){
18950            if(pt){
18951                var el = n.ddel;
18952                var cls;
18953                if(pt == "above"){
18954                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18955                    cls = "x-tree-drag-insert-above";
18956                }else if(pt == "below"){
18957                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18958                    cls = "x-tree-drag-insert-below";
18959                }else{
18960                    returnCls = "x-tree-drop-ok-append";
18961                    cls = "x-tree-drag-append";
18962                }
18963                if(this.lastInsertClass != cls){
18964                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18965                    this.lastInsertClass = cls;
18966                }
18967            }
18968        }
18969        return returnCls;
18970     },
18971     
18972     onNodeOut : function(n, dd, e, data){
18973         this.cancelExpand();
18974         this.removeDropIndicators(n);
18975     },
18976     
18977     onNodeDrop : function(n, dd, e, data){
18978         var point = this.getDropPoint(e, n, dd);
18979         var targetNode = n.node;
18980         targetNode.ui.startDrop();
18981         if(!this.isValidDropPoint(n, point, dd, e, data)){
18982             targetNode.ui.endDrop();
18983             return false;
18984         }
18985         // first try to find the drop node
18986         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
18987         var dropEvent = {
18988             tree : this.tree,
18989             target: targetNode,
18990             data: data,
18991             point: point,
18992             source: dd,
18993             rawEvent: e,
18994             dropNode: dropNode,
18995             cancel: !dropNode   
18996         };
18997         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
18998         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
18999             targetNode.ui.endDrop();
19000             return false;
19001         }
19002         // allow target changing
19003         targetNode = dropEvent.target;
19004         if(point == "append" && !targetNode.isExpanded()){
19005             targetNode.expand(false, null, function(){
19006                 this.completeDrop(dropEvent);
19007             }.createDelegate(this));
19008         }else{
19009             this.completeDrop(dropEvent);
19010         }
19011         return true;
19012     },
19013     
19014     completeDrop : function(de){
19015         var ns = de.dropNode, p = de.point, t = de.target;
19016         if(!(ns instanceof Array)){
19017             ns = [ns];
19018         }
19019         var n;
19020         for(var i = 0, len = ns.length; i < len; i++){
19021             n = ns[i];
19022             if(p == "above"){
19023                 t.parentNode.insertBefore(n, t);
19024             }else if(p == "below"){
19025                 t.parentNode.insertBefore(n, t.nextSibling);
19026             }else{
19027                 t.appendChild(n);
19028             }
19029         }
19030         n.ui.focus();
19031         if(this.tree.hlDrop){
19032             n.ui.highlight();
19033         }
19034         t.ui.endDrop();
19035         this.tree.fireEvent("nodedrop", de);
19036     },
19037     
19038     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
19039         if(this.tree.hlDrop){
19040             dropNode.ui.focus();
19041             dropNode.ui.highlight();
19042         }
19043         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
19044     },
19045     
19046     getTree : function(){
19047         return this.tree;
19048     },
19049     
19050     removeDropIndicators : function(n){
19051         if(n && n.ddel){
19052             var el = n.ddel;
19053             Roo.fly(el).removeClass([
19054                     "x-tree-drag-insert-above",
19055                     "x-tree-drag-insert-below",
19056                     "x-tree-drag-append"]);
19057             this.lastInsertClass = "_noclass";
19058         }
19059     },
19060     
19061     beforeDragDrop : function(target, e, id){
19062         this.cancelExpand();
19063         return true;
19064     },
19065     
19066     afterRepair : function(data){
19067         if(data && Roo.enableFx){
19068             data.node.ui.highlight();
19069         }
19070         this.hideProxy();
19071     }    
19072 });
19073
19074 }
19075 /*
19076  * Based on:
19077  * Ext JS Library 1.1.1
19078  * Copyright(c) 2006-2007, Ext JS, LLC.
19079  *
19080  * Originally Released Under LGPL - original licence link has changed is not relivant.
19081  *
19082  * Fork - LGPL
19083  * <script type="text/javascript">
19084  */
19085  
19086
19087 if(Roo.dd.DragZone){
19088 Roo.tree.TreeDragZone = function(tree, config){
19089     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
19090     this.tree = tree;
19091 };
19092
19093 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
19094     ddGroup : "TreeDD",
19095     
19096     onBeforeDrag : function(data, e){
19097         var n = data.node;
19098         return n && n.draggable && !n.disabled;
19099     },
19100     
19101     onInitDrag : function(e){
19102         var data = this.dragData;
19103         this.tree.getSelectionModel().select(data.node);
19104         this.proxy.update("");
19105         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
19106         this.tree.fireEvent("startdrag", this.tree, data.node, e);
19107     },
19108     
19109     getRepairXY : function(e, data){
19110         return data.node.ui.getDDRepairXY();
19111     },
19112     
19113     onEndDrag : function(data, e){
19114         this.tree.fireEvent("enddrag", this.tree, data.node, e);
19115     },
19116     
19117     onValidDrop : function(dd, e, id){
19118         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
19119         this.hideProxy();
19120     },
19121     
19122     beforeInvalidDrop : function(e, id){
19123         // this scrolls the original position back into view
19124         var sm = this.tree.getSelectionModel();
19125         sm.clearSelections();
19126         sm.select(this.dragData.node);
19127     }
19128 });
19129 }/*
19130  * Based on:
19131  * Ext JS Library 1.1.1
19132  * Copyright(c) 2006-2007, Ext JS, LLC.
19133  *
19134  * Originally Released Under LGPL - original licence link has changed is not relivant.
19135  *
19136  * Fork - LGPL
19137  * <script type="text/javascript">
19138  */
19139 /**
19140  * @class Roo.tree.TreeEditor
19141  * @extends Roo.Editor
19142  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
19143  * as the editor field.
19144  * @constructor
19145  * @param {Object} config (used to be the tree panel.)
19146  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
19147  * 
19148  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
19149  * @cfg {Roo.form.TextField|Object} field The field configuration
19150  *
19151  * 
19152  */
19153 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
19154     var tree = config;
19155     var field;
19156     if (oldconfig) { // old style..
19157         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
19158     } else {
19159         // new style..
19160         tree = config.tree;
19161         config.field = config.field  || {};
19162         config.field.xtype = 'TextField';
19163         field = Roo.factory(config.field, Roo.form);
19164     }
19165     config = config || {};
19166     
19167     
19168     this.addEvents({
19169         /**
19170          * @event beforenodeedit
19171          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
19172          * false from the handler of this event.
19173          * @param {Editor} this
19174          * @param {Roo.tree.Node} node 
19175          */
19176         "beforenodeedit" : true
19177     });
19178     
19179     //Roo.log(config);
19180     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
19181
19182     this.tree = tree;
19183
19184     tree.on('beforeclick', this.beforeNodeClick, this);
19185     tree.getTreeEl().on('mousedown', this.hide, this);
19186     this.on('complete', this.updateNode, this);
19187     this.on('beforestartedit', this.fitToTree, this);
19188     this.on('startedit', this.bindScroll, this, {delay:10});
19189     this.on('specialkey', this.onSpecialKey, this);
19190 };
19191
19192 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
19193     /**
19194      * @cfg {String} alignment
19195      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
19196      */
19197     alignment: "l-l",
19198     // inherit
19199     autoSize: false,
19200     /**
19201      * @cfg {Boolean} hideEl
19202      * True to hide the bound element while the editor is displayed (defaults to false)
19203      */
19204     hideEl : false,
19205     /**
19206      * @cfg {String} cls
19207      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
19208      */
19209     cls: "x-small-editor x-tree-editor",
19210     /**
19211      * @cfg {Boolean} shim
19212      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
19213      */
19214     shim:false,
19215     // inherit
19216     shadow:"frame",
19217     /**
19218      * @cfg {Number} maxWidth
19219      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
19220      * the containing tree element's size, it will be automatically limited for you to the container width, taking
19221      * scroll and client offsets into account prior to each edit.
19222      */
19223     maxWidth: 250,
19224
19225     editDelay : 350,
19226
19227     // private
19228     fitToTree : function(ed, el){
19229         var td = this.tree.getTreeEl().dom, nd = el.dom;
19230         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
19231             td.scrollLeft = nd.offsetLeft;
19232         }
19233         var w = Math.min(
19234                 this.maxWidth,
19235                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
19236         this.setSize(w, '');
19237         
19238         return this.fireEvent('beforenodeedit', this, this.editNode);
19239         
19240     },
19241
19242     // private
19243     triggerEdit : function(node){
19244         this.completeEdit();
19245         this.editNode = node;
19246         this.startEdit(node.ui.textNode, node.text);
19247     },
19248
19249     // private
19250     bindScroll : function(){
19251         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
19252     },
19253
19254     // private
19255     beforeNodeClick : function(node, e){
19256         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19257         this.lastClick = new Date();
19258         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19259             e.stopEvent();
19260             this.triggerEdit(node);
19261             return false;
19262         }
19263         return true;
19264     },
19265
19266     // private
19267     updateNode : function(ed, value){
19268         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19269         this.editNode.setText(value);
19270     },
19271
19272     // private
19273     onHide : function(){
19274         Roo.tree.TreeEditor.superclass.onHide.call(this);
19275         if(this.editNode){
19276             this.editNode.ui.focus();
19277         }
19278     },
19279
19280     // private
19281     onSpecialKey : function(field, e){
19282         var k = e.getKey();
19283         if(k == e.ESC){
19284             e.stopEvent();
19285             this.cancelEdit();
19286         }else if(k == e.ENTER && !e.hasModifier()){
19287             e.stopEvent();
19288             this.completeEdit();
19289         }
19290     }
19291 });//<Script type="text/javascript">
19292 /*
19293  * Based on:
19294  * Ext JS Library 1.1.1
19295  * Copyright(c) 2006-2007, Ext JS, LLC.
19296  *
19297  * Originally Released Under LGPL - original licence link has changed is not relivant.
19298  *
19299  * Fork - LGPL
19300  * <script type="text/javascript">
19301  */
19302  
19303 /**
19304  * Not documented??? - probably should be...
19305  */
19306
19307 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19308     //focus: Roo.emptyFn, // prevent odd scrolling behavior
19309     
19310     renderElements : function(n, a, targetNode, bulkRender){
19311         //consel.log("renderElements?");
19312         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19313
19314         var t = n.getOwnerTree();
19315         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19316         
19317         var cols = t.columns;
19318         var bw = t.borderWidth;
19319         var c = cols[0];
19320         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19321          var cb = typeof a.checked == "boolean";
19322         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19323         var colcls = 'x-t-' + tid + '-c0';
19324         var buf = [
19325             '<li class="x-tree-node">',
19326             
19327                 
19328                 '<div class="x-tree-node-el ', a.cls,'">',
19329                     // extran...
19330                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19331                 
19332                 
19333                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19334                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
19335                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19336                            (a.icon ? ' x-tree-node-inline-icon' : ''),
19337                            (a.iconCls ? ' '+a.iconCls : ''),
19338                            '" unselectable="on" />',
19339                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
19340                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
19341                              
19342                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19343                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19344                             '<span unselectable="on" qtip="' + tx + '">',
19345                              tx,
19346                              '</span></a>' ,
19347                     '</div>',
19348                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19349                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19350                  ];
19351         for(var i = 1, len = cols.length; i < len; i++){
19352             c = cols[i];
19353             colcls = 'x-t-' + tid + '-c' +i;
19354             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19355             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19356                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19357                       "</div>");
19358          }
19359          
19360          buf.push(
19361             '</a>',
19362             '<div class="x-clear"></div></div>',
19363             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19364             "</li>");
19365         
19366         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19367             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19368                                 n.nextSibling.ui.getEl(), buf.join(""));
19369         }else{
19370             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19371         }
19372         var el = this.wrap.firstChild;
19373         this.elRow = el;
19374         this.elNode = el.firstChild;
19375         this.ranchor = el.childNodes[1];
19376         this.ctNode = this.wrap.childNodes[1];
19377         var cs = el.firstChild.childNodes;
19378         this.indentNode = cs[0];
19379         this.ecNode = cs[1];
19380         this.iconNode = cs[2];
19381         var index = 3;
19382         if(cb){
19383             this.checkbox = cs[3];
19384             index++;
19385         }
19386         this.anchor = cs[index];
19387         
19388         this.textNode = cs[index].firstChild;
19389         
19390         //el.on("click", this.onClick, this);
19391         //el.on("dblclick", this.onDblClick, this);
19392         
19393         
19394        // console.log(this);
19395     },
19396     initEvents : function(){
19397         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19398         
19399             
19400         var a = this.ranchor;
19401
19402         var el = Roo.get(a);
19403
19404         if(Roo.isOpera){ // opera render bug ignores the CSS
19405             el.setStyle("text-decoration", "none");
19406         }
19407
19408         el.on("click", this.onClick, this);
19409         el.on("dblclick", this.onDblClick, this);
19410         el.on("contextmenu", this.onContextMenu, this);
19411         
19412     },
19413     
19414     /*onSelectedChange : function(state){
19415         if(state){
19416             this.focus();
19417             this.addClass("x-tree-selected");
19418         }else{
19419             //this.blur();
19420             this.removeClass("x-tree-selected");
19421         }
19422     },*/
19423     addClass : function(cls){
19424         if(this.elRow){
19425             Roo.fly(this.elRow).addClass(cls);
19426         }
19427         
19428     },
19429     
19430     
19431     removeClass : function(cls){
19432         if(this.elRow){
19433             Roo.fly(this.elRow).removeClass(cls);
19434         }
19435     }
19436
19437     
19438     
19439 });//<Script type="text/javascript">
19440
19441 /*
19442  * Based on:
19443  * Ext JS Library 1.1.1
19444  * Copyright(c) 2006-2007, Ext JS, LLC.
19445  *
19446  * Originally Released Under LGPL - original licence link has changed is not relivant.
19447  *
19448  * Fork - LGPL
19449  * <script type="text/javascript">
19450  */
19451  
19452
19453 /**
19454  * @class Roo.tree.ColumnTree
19455  * @extends Roo.data.TreePanel
19456  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19457  * @cfg {int} borderWidth  compined right/left border allowance
19458  * @constructor
19459  * @param {String/HTMLElement/Element} el The container element
19460  * @param {Object} config
19461  */
19462 Roo.tree.ColumnTree =  function(el, config)
19463 {
19464    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19465    this.addEvents({
19466         /**
19467         * @event resize
19468         * Fire this event on a container when it resizes
19469         * @param {int} w Width
19470         * @param {int} h Height
19471         */
19472        "resize" : true
19473     });
19474     this.on('resize', this.onResize, this);
19475 };
19476
19477 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19478     //lines:false,
19479     
19480     
19481     borderWidth: Roo.isBorderBox ? 0 : 2, 
19482     headEls : false,
19483     
19484     render : function(){
19485         // add the header.....
19486        
19487         Roo.tree.ColumnTree.superclass.render.apply(this);
19488         
19489         this.el.addClass('x-column-tree');
19490         
19491         this.headers = this.el.createChild(
19492             {cls:'x-tree-headers'},this.innerCt.dom);
19493    
19494         var cols = this.columns, c;
19495         var totalWidth = 0;
19496         this.headEls = [];
19497         var  len = cols.length;
19498         for(var i = 0; i < len; i++){
19499              c = cols[i];
19500              totalWidth += c.width;
19501             this.headEls.push(this.headers.createChild({
19502                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19503                  cn: {
19504                      cls:'x-tree-hd-text',
19505                      html: c.header
19506                  },
19507                  style:'width:'+(c.width-this.borderWidth)+'px;'
19508              }));
19509         }
19510         this.headers.createChild({cls:'x-clear'});
19511         // prevent floats from wrapping when clipped
19512         this.headers.setWidth(totalWidth);
19513         //this.innerCt.setWidth(totalWidth);
19514         this.innerCt.setStyle({ overflow: 'auto' });
19515         this.onResize(this.width, this.height);
19516              
19517         
19518     },
19519     onResize : function(w,h)
19520     {
19521         this.height = h;
19522         this.width = w;
19523         // resize cols..
19524         this.innerCt.setWidth(this.width);
19525         this.innerCt.setHeight(this.height-20);
19526         
19527         // headers...
19528         var cols = this.columns, c;
19529         var totalWidth = 0;
19530         var expEl = false;
19531         var len = cols.length;
19532         for(var i = 0; i < len; i++){
19533             c = cols[i];
19534             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19535                 // it's the expander..
19536                 expEl  = this.headEls[i];
19537                 continue;
19538             }
19539             totalWidth += c.width;
19540             
19541         }
19542         if (expEl) {
19543             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19544         }
19545         this.headers.setWidth(w-20);
19546
19547         
19548         
19549         
19550     }
19551 });
19552 /*
19553  * Based on:
19554  * Ext JS Library 1.1.1
19555  * Copyright(c) 2006-2007, Ext JS, LLC.
19556  *
19557  * Originally Released Under LGPL - original licence link has changed is not relivant.
19558  *
19559  * Fork - LGPL
19560  * <script type="text/javascript">
19561  */
19562  
19563 /**
19564  * @class Roo.menu.Menu
19565  * @extends Roo.util.Observable
19566  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19567  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19568  * @constructor
19569  * Creates a new Menu
19570  * @param {Object} config Configuration options
19571  */
19572 Roo.menu.Menu = function(config){
19573     Roo.apply(this, config);
19574     this.id = this.id || Roo.id();
19575     this.addEvents({
19576         /**
19577          * @event beforeshow
19578          * Fires before this menu is displayed
19579          * @param {Roo.menu.Menu} this
19580          */
19581         beforeshow : true,
19582         /**
19583          * @event beforehide
19584          * Fires before this menu is hidden
19585          * @param {Roo.menu.Menu} this
19586          */
19587         beforehide : true,
19588         /**
19589          * @event show
19590          * Fires after this menu is displayed
19591          * @param {Roo.menu.Menu} this
19592          */
19593         show : true,
19594         /**
19595          * @event hide
19596          * Fires after this menu is hidden
19597          * @param {Roo.menu.Menu} this
19598          */
19599         hide : true,
19600         /**
19601          * @event click
19602          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19603          * @param {Roo.menu.Menu} this
19604          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19605          * @param {Roo.EventObject} e
19606          */
19607         click : true,
19608         /**
19609          * @event mouseover
19610          * Fires when the mouse is hovering over this menu
19611          * @param {Roo.menu.Menu} this
19612          * @param {Roo.EventObject} e
19613          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19614          */
19615         mouseover : true,
19616         /**
19617          * @event mouseout
19618          * Fires when the mouse exits this menu
19619          * @param {Roo.menu.Menu} this
19620          * @param {Roo.EventObject} e
19621          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19622          */
19623         mouseout : true,
19624         /**
19625          * @event itemclick
19626          * Fires when a menu item contained in this menu is clicked
19627          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19628          * @param {Roo.EventObject} e
19629          */
19630         itemclick: true
19631     });
19632     if (this.registerMenu) {
19633         Roo.menu.MenuMgr.register(this);
19634     }
19635     
19636     var mis = this.items;
19637     this.items = new Roo.util.MixedCollection();
19638     if(mis){
19639         this.add.apply(this, mis);
19640     }
19641 };
19642
19643 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19644     /**
19645      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19646      */
19647     minWidth : 120,
19648     /**
19649      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19650      * for bottom-right shadow (defaults to "sides")
19651      */
19652     shadow : "sides",
19653     /**
19654      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19655      * this menu (defaults to "tl-tr?")
19656      */
19657     subMenuAlign : "tl-tr?",
19658     /**
19659      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19660      * relative to its element of origin (defaults to "tl-bl?")
19661      */
19662     defaultAlign : "tl-bl?",
19663     /**
19664      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19665      */
19666     allowOtherMenus : false,
19667     /**
19668      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19669      */
19670     registerMenu : true,
19671
19672     hidden:true,
19673
19674     // private
19675     render : function(){
19676         if(this.el){
19677             return;
19678         }
19679         var el = this.el = new Roo.Layer({
19680             cls: "x-menu",
19681             shadow:this.shadow,
19682             constrain: false,
19683             parentEl: this.parentEl || document.body,
19684             zindex:15000
19685         });
19686
19687         this.keyNav = new Roo.menu.MenuNav(this);
19688
19689         if(this.plain){
19690             el.addClass("x-menu-plain");
19691         }
19692         if(this.cls){
19693             el.addClass(this.cls);
19694         }
19695         // generic focus element
19696         this.focusEl = el.createChild({
19697             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19698         });
19699         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19700         ul.on("click", this.onClick, this);
19701         ul.on("mouseover", this.onMouseOver, this);
19702         ul.on("mouseout", this.onMouseOut, this);
19703         this.items.each(function(item){
19704             var li = document.createElement("li");
19705             li.className = "x-menu-list-item";
19706             ul.dom.appendChild(li);
19707             item.render(li, this);
19708         }, this);
19709         this.ul = ul;
19710         this.autoWidth();
19711     },
19712
19713     // private
19714     autoWidth : function(){
19715         var el = this.el, ul = this.ul;
19716         if(!el){
19717             return;
19718         }
19719         var w = this.width;
19720         if(w){
19721             el.setWidth(w);
19722         }else if(Roo.isIE){
19723             el.setWidth(this.minWidth);
19724             var t = el.dom.offsetWidth; // force recalc
19725             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19726         }
19727     },
19728
19729     // private
19730     delayAutoWidth : function(){
19731         if(this.rendered){
19732             if(!this.awTask){
19733                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19734             }
19735             this.awTask.delay(20);
19736         }
19737     },
19738
19739     // private
19740     findTargetItem : function(e){
19741         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19742         if(t && t.menuItemId){
19743             return this.items.get(t.menuItemId);
19744         }
19745     },
19746
19747     // private
19748     onClick : function(e){
19749         var t;
19750         if(t = this.findTargetItem(e)){
19751             t.onClick(e);
19752             this.fireEvent("click", this, t, e);
19753         }
19754     },
19755
19756     // private
19757     setActiveItem : function(item, autoExpand){
19758         if(item != this.activeItem){
19759             if(this.activeItem){
19760                 this.activeItem.deactivate();
19761             }
19762             this.activeItem = item;
19763             item.activate(autoExpand);
19764         }else if(autoExpand){
19765             item.expandMenu();
19766         }
19767     },
19768
19769     // private
19770     tryActivate : function(start, step){
19771         var items = this.items;
19772         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19773             var item = items.get(i);
19774             if(!item.disabled && item.canActivate){
19775                 this.setActiveItem(item, false);
19776                 return item;
19777             }
19778         }
19779         return false;
19780     },
19781
19782     // private
19783     onMouseOver : function(e){
19784         var t;
19785         if(t = this.findTargetItem(e)){
19786             if(t.canActivate && !t.disabled){
19787                 this.setActiveItem(t, true);
19788             }
19789         }
19790         this.fireEvent("mouseover", this, e, t);
19791     },
19792
19793     // private
19794     onMouseOut : function(e){
19795         var t;
19796         if(t = this.findTargetItem(e)){
19797             if(t == this.activeItem && t.shouldDeactivate(e)){
19798                 this.activeItem.deactivate();
19799                 delete this.activeItem;
19800             }
19801         }
19802         this.fireEvent("mouseout", this, e, t);
19803     },
19804
19805     /**
19806      * Read-only.  Returns true if the menu is currently displayed, else false.
19807      * @type Boolean
19808      */
19809     isVisible : function(){
19810         return this.el && !this.hidden;
19811     },
19812
19813     /**
19814      * Displays this menu relative to another element
19815      * @param {String/HTMLElement/Roo.Element} element The element to align to
19816      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19817      * the element (defaults to this.defaultAlign)
19818      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19819      */
19820     show : function(el, pos, parentMenu){
19821         this.parentMenu = parentMenu;
19822         if(!this.el){
19823             this.render();
19824         }
19825         this.fireEvent("beforeshow", this);
19826         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19827     },
19828
19829     /**
19830      * Displays this menu at a specific xy position
19831      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19832      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19833      */
19834     showAt : function(xy, parentMenu, /* private: */_e){
19835         this.parentMenu = parentMenu;
19836         if(!this.el){
19837             this.render();
19838         }
19839         if(_e !== false){
19840             this.fireEvent("beforeshow", this);
19841             xy = this.el.adjustForConstraints(xy);
19842         }
19843         this.el.setXY(xy);
19844         this.el.show();
19845         this.hidden = false;
19846         this.focus();
19847         this.fireEvent("show", this);
19848     },
19849
19850     focus : function(){
19851         if(!this.hidden){
19852             this.doFocus.defer(50, this);
19853         }
19854     },
19855
19856     doFocus : function(){
19857         if(!this.hidden){
19858             this.focusEl.focus();
19859         }
19860     },
19861
19862     /**
19863      * Hides this menu and optionally all parent menus
19864      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19865      */
19866     hide : function(deep){
19867         if(this.el && this.isVisible()){
19868             this.fireEvent("beforehide", this);
19869             if(this.activeItem){
19870                 this.activeItem.deactivate();
19871                 this.activeItem = null;
19872             }
19873             this.el.hide();
19874             this.hidden = true;
19875             this.fireEvent("hide", this);
19876         }
19877         if(deep === true && this.parentMenu){
19878             this.parentMenu.hide(true);
19879         }
19880     },
19881
19882     /**
19883      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19884      * Any of the following are valid:
19885      * <ul>
19886      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19887      * <li>An HTMLElement object which will be converted to a menu item</li>
19888      * <li>A menu item config object that will be created as a new menu item</li>
19889      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19890      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19891      * </ul>
19892      * Usage:
19893      * <pre><code>
19894 // Create the menu
19895 var menu = new Roo.menu.Menu();
19896
19897 // Create a menu item to add by reference
19898 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19899
19900 // Add a bunch of items at once using different methods.
19901 // Only the last item added will be returned.
19902 var item = menu.add(
19903     menuItem,                // add existing item by ref
19904     'Dynamic Item',          // new TextItem
19905     '-',                     // new separator
19906     { text: 'Config Item' }  // new item by config
19907 );
19908 </code></pre>
19909      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19910      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19911      */
19912     add : function(){
19913         var a = arguments, l = a.length, item;
19914         for(var i = 0; i < l; i++){
19915             var el = a[i];
19916             if ((typeof(el) == "object") && el.xtype && el.xns) {
19917                 el = Roo.factory(el, Roo.menu);
19918             }
19919             
19920             if(el.render){ // some kind of Item
19921                 item = this.addItem(el);
19922             }else if(typeof el == "string"){ // string
19923                 if(el == "separator" || el == "-"){
19924                     item = this.addSeparator();
19925                 }else{
19926                     item = this.addText(el);
19927                 }
19928             }else if(el.tagName || el.el){ // element
19929                 item = this.addElement(el);
19930             }else if(typeof el == "object"){ // must be menu item config?
19931                 item = this.addMenuItem(el);
19932             }
19933         }
19934         return item;
19935     },
19936
19937     /**
19938      * Returns this menu's underlying {@link Roo.Element} object
19939      * @return {Roo.Element} The element
19940      */
19941     getEl : function(){
19942         if(!this.el){
19943             this.render();
19944         }
19945         return this.el;
19946     },
19947
19948     /**
19949      * Adds a separator bar to the menu
19950      * @return {Roo.menu.Item} The menu item that was added
19951      */
19952     addSeparator : function(){
19953         return this.addItem(new Roo.menu.Separator());
19954     },
19955
19956     /**
19957      * Adds an {@link Roo.Element} object to the menu
19958      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19959      * @return {Roo.menu.Item} The menu item that was added
19960      */
19961     addElement : function(el){
19962         return this.addItem(new Roo.menu.BaseItem(el));
19963     },
19964
19965     /**
19966      * Adds an existing object based on {@link Roo.menu.Item} to the menu
19967      * @param {Roo.menu.Item} item The menu item to add
19968      * @return {Roo.menu.Item} The menu item that was added
19969      */
19970     addItem : function(item){
19971         this.items.add(item);
19972         if(this.ul){
19973             var li = document.createElement("li");
19974             li.className = "x-menu-list-item";
19975             this.ul.dom.appendChild(li);
19976             item.render(li, this);
19977             this.delayAutoWidth();
19978         }
19979         return item;
19980     },
19981
19982     /**
19983      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
19984      * @param {Object} config A MenuItem config object
19985      * @return {Roo.menu.Item} The menu item that was added
19986      */
19987     addMenuItem : function(config){
19988         if(!(config instanceof Roo.menu.Item)){
19989             if(typeof config.checked == "boolean"){ // must be check menu item config?
19990                 config = new Roo.menu.CheckItem(config);
19991             }else{
19992                 config = new Roo.menu.Item(config);
19993             }
19994         }
19995         return this.addItem(config);
19996     },
19997
19998     /**
19999      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
20000      * @param {String} text The text to display in the menu item
20001      * @return {Roo.menu.Item} The menu item that was added
20002      */
20003     addText : function(text){
20004         return this.addItem(new Roo.menu.TextItem({ text : text }));
20005     },
20006
20007     /**
20008      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
20009      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
20010      * @param {Roo.menu.Item} item The menu item to add
20011      * @return {Roo.menu.Item} The menu item that was added
20012      */
20013     insert : function(index, item){
20014         this.items.insert(index, item);
20015         if(this.ul){
20016             var li = document.createElement("li");
20017             li.className = "x-menu-list-item";
20018             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
20019             item.render(li, this);
20020             this.delayAutoWidth();
20021         }
20022         return item;
20023     },
20024
20025     /**
20026      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
20027      * @param {Roo.menu.Item} item The menu item to remove
20028      */
20029     remove : function(item){
20030         this.items.removeKey(item.id);
20031         item.destroy();
20032     },
20033
20034     /**
20035      * Removes and destroys all items in the menu
20036      */
20037     removeAll : function(){
20038         var f;
20039         while(f = this.items.first()){
20040             this.remove(f);
20041         }
20042     }
20043 });
20044
20045 // MenuNav is a private utility class used internally by the Menu
20046 Roo.menu.MenuNav = function(menu){
20047     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
20048     this.scope = this.menu = menu;
20049 };
20050
20051 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
20052     doRelay : function(e, h){
20053         var k = e.getKey();
20054         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
20055             this.menu.tryActivate(0, 1);
20056             return false;
20057         }
20058         return h.call(this.scope || this, e, this.menu);
20059     },
20060
20061     up : function(e, m){
20062         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
20063             m.tryActivate(m.items.length-1, -1);
20064         }
20065     },
20066
20067     down : function(e, m){
20068         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
20069             m.tryActivate(0, 1);
20070         }
20071     },
20072
20073     right : function(e, m){
20074         if(m.activeItem){
20075             m.activeItem.expandMenu(true);
20076         }
20077     },
20078
20079     left : function(e, m){
20080         m.hide();
20081         if(m.parentMenu && m.parentMenu.activeItem){
20082             m.parentMenu.activeItem.activate();
20083         }
20084     },
20085
20086     enter : function(e, m){
20087         if(m.activeItem){
20088             e.stopPropagation();
20089             m.activeItem.onClick(e);
20090             m.fireEvent("click", this, m.activeItem);
20091             return true;
20092         }
20093     }
20094 });/*
20095  * Based on:
20096  * Ext JS Library 1.1.1
20097  * Copyright(c) 2006-2007, Ext JS, LLC.
20098  *
20099  * Originally Released Under LGPL - original licence link has changed is not relivant.
20100  *
20101  * Fork - LGPL
20102  * <script type="text/javascript">
20103  */
20104  
20105 /**
20106  * @class Roo.menu.MenuMgr
20107  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
20108  * @singleton
20109  */
20110 Roo.menu.MenuMgr = function(){
20111    var menus, active, groups = {}, attached = false, lastShow = new Date();
20112
20113    // private - called when first menu is created
20114    function init(){
20115        menus = {};
20116        active = new Roo.util.MixedCollection();
20117        Roo.get(document).addKeyListener(27, function(){
20118            if(active.length > 0){
20119                hideAll();
20120            }
20121        });
20122    }
20123
20124    // private
20125    function hideAll(){
20126        if(active && active.length > 0){
20127            var c = active.clone();
20128            c.each(function(m){
20129                m.hide();
20130            });
20131        }
20132    }
20133
20134    // private
20135    function onHide(m){
20136        active.remove(m);
20137        if(active.length < 1){
20138            Roo.get(document).un("mousedown", onMouseDown);
20139            attached = false;
20140        }
20141    }
20142
20143    // private
20144    function onShow(m){
20145        var last = active.last();
20146        lastShow = new Date();
20147        active.add(m);
20148        if(!attached){
20149            Roo.get(document).on("mousedown", onMouseDown);
20150            attached = true;
20151        }
20152        if(m.parentMenu){
20153           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
20154           m.parentMenu.activeChild = m;
20155        }else if(last && last.isVisible()){
20156           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
20157        }
20158    }
20159
20160    // private
20161    function onBeforeHide(m){
20162        if(m.activeChild){
20163            m.activeChild.hide();
20164        }
20165        if(m.autoHideTimer){
20166            clearTimeout(m.autoHideTimer);
20167            delete m.autoHideTimer;
20168        }
20169    }
20170
20171    // private
20172    function onBeforeShow(m){
20173        var pm = m.parentMenu;
20174        if(!pm && !m.allowOtherMenus){
20175            hideAll();
20176        }else if(pm && pm.activeChild && active != m){
20177            pm.activeChild.hide();
20178        }
20179    }
20180
20181    // private
20182    function onMouseDown(e){
20183        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
20184            hideAll();
20185        }
20186    }
20187
20188    // private
20189    function onBeforeCheck(mi, state){
20190        if(state){
20191            var g = groups[mi.group];
20192            for(var i = 0, l = g.length; i < l; i++){
20193                if(g[i] != mi){
20194                    g[i].setChecked(false);
20195                }
20196            }
20197        }
20198    }
20199
20200    return {
20201
20202        /**
20203         * Hides all menus that are currently visible
20204         */
20205        hideAll : function(){
20206             hideAll();  
20207        },
20208
20209        // private
20210        register : function(menu){
20211            if(!menus){
20212                init();
20213            }
20214            menus[menu.id] = menu;
20215            menu.on("beforehide", onBeforeHide);
20216            menu.on("hide", onHide);
20217            menu.on("beforeshow", onBeforeShow);
20218            menu.on("show", onShow);
20219            var g = menu.group;
20220            if(g && menu.events["checkchange"]){
20221                if(!groups[g]){
20222                    groups[g] = [];
20223                }
20224                groups[g].push(menu);
20225                menu.on("checkchange", onCheck);
20226            }
20227        },
20228
20229         /**
20230          * Returns a {@link Roo.menu.Menu} object
20231          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
20232          * be used to generate and return a new Menu instance.
20233          */
20234        get : function(menu){
20235            if(typeof menu == "string"){ // menu id
20236                return menus[menu];
20237            }else if(menu.events){  // menu instance
20238                return menu;
20239            }else if(typeof menu.length == 'number'){ // array of menu items?
20240                return new Roo.menu.Menu({items:menu});
20241            }else{ // otherwise, must be a config
20242                return new Roo.menu.Menu(menu);
20243            }
20244        },
20245
20246        // private
20247        unregister : function(menu){
20248            delete menus[menu.id];
20249            menu.un("beforehide", onBeforeHide);
20250            menu.un("hide", onHide);
20251            menu.un("beforeshow", onBeforeShow);
20252            menu.un("show", onShow);
20253            var g = menu.group;
20254            if(g && menu.events["checkchange"]){
20255                groups[g].remove(menu);
20256                menu.un("checkchange", onCheck);
20257            }
20258        },
20259
20260        // private
20261        registerCheckable : function(menuItem){
20262            var g = menuItem.group;
20263            if(g){
20264                if(!groups[g]){
20265                    groups[g] = [];
20266                }
20267                groups[g].push(menuItem);
20268                menuItem.on("beforecheckchange", onBeforeCheck);
20269            }
20270        },
20271
20272        // private
20273        unregisterCheckable : function(menuItem){
20274            var g = menuItem.group;
20275            if(g){
20276                groups[g].remove(menuItem);
20277                menuItem.un("beforecheckchange", onBeforeCheck);
20278            }
20279        }
20280    };
20281 }();/*
20282  * Based on:
20283  * Ext JS Library 1.1.1
20284  * Copyright(c) 2006-2007, Ext JS, LLC.
20285  *
20286  * Originally Released Under LGPL - original licence link has changed is not relivant.
20287  *
20288  * Fork - LGPL
20289  * <script type="text/javascript">
20290  */
20291  
20292
20293 /**
20294  * @class Roo.menu.BaseItem
20295  * @extends Roo.Component
20296  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
20297  * management and base configuration options shared by all menu components.
20298  * @constructor
20299  * Creates a new BaseItem
20300  * @param {Object} config Configuration options
20301  */
20302 Roo.menu.BaseItem = function(config){
20303     Roo.menu.BaseItem.superclass.constructor.call(this, config);
20304
20305     this.addEvents({
20306         /**
20307          * @event click
20308          * Fires when this item is clicked
20309          * @param {Roo.menu.BaseItem} this
20310          * @param {Roo.EventObject} e
20311          */
20312         click: true,
20313         /**
20314          * @event activate
20315          * Fires when this item is activated
20316          * @param {Roo.menu.BaseItem} this
20317          */
20318         activate : true,
20319         /**
20320          * @event deactivate
20321          * Fires when this item is deactivated
20322          * @param {Roo.menu.BaseItem} this
20323          */
20324         deactivate : true
20325     });
20326
20327     if(this.handler){
20328         this.on("click", this.handler, this.scope, true);
20329     }
20330 };
20331
20332 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20333     /**
20334      * @cfg {Function} handler
20335      * A function that will handle the click event of this menu item (defaults to undefined)
20336      */
20337     /**
20338      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20339      */
20340     canActivate : false,
20341     /**
20342      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20343      */
20344     activeClass : "x-menu-item-active",
20345     /**
20346      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20347      */
20348     hideOnClick : true,
20349     /**
20350      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20351      */
20352     hideDelay : 100,
20353
20354     // private
20355     ctype: "Roo.menu.BaseItem",
20356
20357     // private
20358     actionMode : "container",
20359
20360     // private
20361     render : function(container, parentMenu){
20362         this.parentMenu = parentMenu;
20363         Roo.menu.BaseItem.superclass.render.call(this, container);
20364         this.container.menuItemId = this.id;
20365     },
20366
20367     // private
20368     onRender : function(container, position){
20369         this.el = Roo.get(this.el);
20370         container.dom.appendChild(this.el.dom);
20371     },
20372
20373     // private
20374     onClick : function(e){
20375         if(!this.disabled && this.fireEvent("click", this, e) !== false
20376                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20377             this.handleClick(e);
20378         }else{
20379             e.stopEvent();
20380         }
20381     },
20382
20383     // private
20384     activate : function(){
20385         if(this.disabled){
20386             return false;
20387         }
20388         var li = this.container;
20389         li.addClass(this.activeClass);
20390         this.region = li.getRegion().adjust(2, 2, -2, -2);
20391         this.fireEvent("activate", this);
20392         return true;
20393     },
20394
20395     // private
20396     deactivate : function(){
20397         this.container.removeClass(this.activeClass);
20398         this.fireEvent("deactivate", this);
20399     },
20400
20401     // private
20402     shouldDeactivate : function(e){
20403         return !this.region || !this.region.contains(e.getPoint());
20404     },
20405
20406     // private
20407     handleClick : function(e){
20408         if(this.hideOnClick){
20409             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20410         }
20411     },
20412
20413     // private
20414     expandMenu : function(autoActivate){
20415         // do nothing
20416     },
20417
20418     // private
20419     hideMenu : function(){
20420         // do nothing
20421     }
20422 });/*
20423  * Based on:
20424  * Ext JS Library 1.1.1
20425  * Copyright(c) 2006-2007, Ext JS, LLC.
20426  *
20427  * Originally Released Under LGPL - original licence link has changed is not relivant.
20428  *
20429  * Fork - LGPL
20430  * <script type="text/javascript">
20431  */
20432  
20433 /**
20434  * @class Roo.menu.Adapter
20435  * @extends Roo.menu.BaseItem
20436  * 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.
20437  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20438  * @constructor
20439  * Creates a new Adapter
20440  * @param {Object} config Configuration options
20441  */
20442 Roo.menu.Adapter = function(component, config){
20443     Roo.menu.Adapter.superclass.constructor.call(this, config);
20444     this.component = component;
20445 };
20446 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20447     // private
20448     canActivate : true,
20449
20450     // private
20451     onRender : function(container, position){
20452         this.component.render(container);
20453         this.el = this.component.getEl();
20454     },
20455
20456     // private
20457     activate : function(){
20458         if(this.disabled){
20459             return false;
20460         }
20461         this.component.focus();
20462         this.fireEvent("activate", this);
20463         return true;
20464     },
20465
20466     // private
20467     deactivate : function(){
20468         this.fireEvent("deactivate", this);
20469     },
20470
20471     // private
20472     disable : function(){
20473         this.component.disable();
20474         Roo.menu.Adapter.superclass.disable.call(this);
20475     },
20476
20477     // private
20478     enable : function(){
20479         this.component.enable();
20480         Roo.menu.Adapter.superclass.enable.call(this);
20481     }
20482 });/*
20483  * Based on:
20484  * Ext JS Library 1.1.1
20485  * Copyright(c) 2006-2007, Ext JS, LLC.
20486  *
20487  * Originally Released Under LGPL - original licence link has changed is not relivant.
20488  *
20489  * Fork - LGPL
20490  * <script type="text/javascript">
20491  */
20492
20493 /**
20494  * @class Roo.menu.TextItem
20495  * @extends Roo.menu.BaseItem
20496  * Adds a static text string to a menu, usually used as either a heading or group separator.
20497  * Note: old style constructor with text is still supported.
20498  * 
20499  * @constructor
20500  * Creates a new TextItem
20501  * @param {Object} cfg Configuration
20502  */
20503 Roo.menu.TextItem = function(cfg){
20504     if (typeof(cfg) == 'string') {
20505         this.text = cfg;
20506     } else {
20507         Roo.apply(this,cfg);
20508     }
20509     
20510     Roo.menu.TextItem.superclass.constructor.call(this);
20511 };
20512
20513 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20514     /**
20515      * @cfg {Boolean} text Text to show on item.
20516      */
20517     text : '',
20518     
20519     /**
20520      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20521      */
20522     hideOnClick : false,
20523     /**
20524      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20525      */
20526     itemCls : "x-menu-text",
20527
20528     // private
20529     onRender : function(){
20530         var s = document.createElement("span");
20531         s.className = this.itemCls;
20532         s.innerHTML = this.text;
20533         this.el = s;
20534         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20535     }
20536 });/*
20537  * Based on:
20538  * Ext JS Library 1.1.1
20539  * Copyright(c) 2006-2007, Ext JS, LLC.
20540  *
20541  * Originally Released Under LGPL - original licence link has changed is not relivant.
20542  *
20543  * Fork - LGPL
20544  * <script type="text/javascript">
20545  */
20546
20547 /**
20548  * @class Roo.menu.Separator
20549  * @extends Roo.menu.BaseItem
20550  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20551  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20552  * @constructor
20553  * @param {Object} config Configuration options
20554  */
20555 Roo.menu.Separator = function(config){
20556     Roo.menu.Separator.superclass.constructor.call(this, config);
20557 };
20558
20559 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20560     /**
20561      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20562      */
20563     itemCls : "x-menu-sep",
20564     /**
20565      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20566      */
20567     hideOnClick : false,
20568
20569     // private
20570     onRender : function(li){
20571         var s = document.createElement("span");
20572         s.className = this.itemCls;
20573         s.innerHTML = "&#160;";
20574         this.el = s;
20575         li.addClass("x-menu-sep-li");
20576         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20577     }
20578 });/*
20579  * Based on:
20580  * Ext JS Library 1.1.1
20581  * Copyright(c) 2006-2007, Ext JS, LLC.
20582  *
20583  * Originally Released Under LGPL - original licence link has changed is not relivant.
20584  *
20585  * Fork - LGPL
20586  * <script type="text/javascript">
20587  */
20588 /**
20589  * @class Roo.menu.Item
20590  * @extends Roo.menu.BaseItem
20591  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20592  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20593  * activation and click handling.
20594  * @constructor
20595  * Creates a new Item
20596  * @param {Object} config Configuration options
20597  */
20598 Roo.menu.Item = function(config){
20599     Roo.menu.Item.superclass.constructor.call(this, config);
20600     if(this.menu){
20601         this.menu = Roo.menu.MenuMgr.get(this.menu);
20602     }
20603 };
20604 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20605     
20606     /**
20607      * @cfg {String} text
20608      * The text to show on the menu item.
20609      */
20610     text: '',
20611      /**
20612      * @cfg {String} HTML to render in menu
20613      * The text to show on the menu item (HTML version).
20614      */
20615     html: '',
20616     /**
20617      * @cfg {String} icon
20618      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20619      */
20620     icon: undefined,
20621     /**
20622      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20623      */
20624     itemCls : "x-menu-item",
20625     /**
20626      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20627      */
20628     canActivate : true,
20629     /**
20630      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20631      */
20632     showDelay: 200,
20633     // doc'd in BaseItem
20634     hideDelay: 200,
20635
20636     // private
20637     ctype: "Roo.menu.Item",
20638     
20639     // private
20640     onRender : function(container, position){
20641         var el = document.createElement("a");
20642         el.hideFocus = true;
20643         el.unselectable = "on";
20644         el.href = this.href || "#";
20645         if(this.hrefTarget){
20646             el.target = this.hrefTarget;
20647         }
20648         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20649         
20650         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20651         
20652         el.innerHTML = String.format(
20653                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20654                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20655         this.el = el;
20656         Roo.menu.Item.superclass.onRender.call(this, container, position);
20657     },
20658
20659     /**
20660      * Sets the text to display in this menu item
20661      * @param {String} text The text to display
20662      * @param {Boolean} isHTML true to indicate text is pure html.
20663      */
20664     setText : function(text, isHTML){
20665         if (isHTML) {
20666             this.html = text;
20667         } else {
20668             this.text = text;
20669             this.html = '';
20670         }
20671         if(this.rendered){
20672             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20673      
20674             this.el.update(String.format(
20675                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20676                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20677             this.parentMenu.autoWidth();
20678         }
20679     },
20680
20681     // private
20682     handleClick : function(e){
20683         if(!this.href){ // if no link defined, stop the event automatically
20684             e.stopEvent();
20685         }
20686         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20687     },
20688
20689     // private
20690     activate : function(autoExpand){
20691         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20692             this.focus();
20693             if(autoExpand){
20694                 this.expandMenu();
20695             }
20696         }
20697         return true;
20698     },
20699
20700     // private
20701     shouldDeactivate : function(e){
20702         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20703             if(this.menu && this.menu.isVisible()){
20704                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20705             }
20706             return true;
20707         }
20708         return false;
20709     },
20710
20711     // private
20712     deactivate : function(){
20713         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20714         this.hideMenu();
20715     },
20716
20717     // private
20718     expandMenu : function(autoActivate){
20719         if(!this.disabled && this.menu){
20720             clearTimeout(this.hideTimer);
20721             delete this.hideTimer;
20722             if(!this.menu.isVisible() && !this.showTimer){
20723                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20724             }else if (this.menu.isVisible() && autoActivate){
20725                 this.menu.tryActivate(0, 1);
20726             }
20727         }
20728     },
20729
20730     // private
20731     deferExpand : function(autoActivate){
20732         delete this.showTimer;
20733         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20734         if(autoActivate){
20735             this.menu.tryActivate(0, 1);
20736         }
20737     },
20738
20739     // private
20740     hideMenu : function(){
20741         clearTimeout(this.showTimer);
20742         delete this.showTimer;
20743         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20744             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20745         }
20746     },
20747
20748     // private
20749     deferHide : function(){
20750         delete this.hideTimer;
20751         this.menu.hide();
20752     }
20753 });/*
20754  * Based on:
20755  * Ext JS Library 1.1.1
20756  * Copyright(c) 2006-2007, Ext JS, LLC.
20757  *
20758  * Originally Released Under LGPL - original licence link has changed is not relivant.
20759  *
20760  * Fork - LGPL
20761  * <script type="text/javascript">
20762  */
20763  
20764 /**
20765  * @class Roo.menu.CheckItem
20766  * @extends Roo.menu.Item
20767  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20768  * @constructor
20769  * Creates a new CheckItem
20770  * @param {Object} config Configuration options
20771  */
20772 Roo.menu.CheckItem = function(config){
20773     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20774     this.addEvents({
20775         /**
20776          * @event beforecheckchange
20777          * Fires before the checked value is set, providing an opportunity to cancel if needed
20778          * @param {Roo.menu.CheckItem} this
20779          * @param {Boolean} checked The new checked value that will be set
20780          */
20781         "beforecheckchange" : true,
20782         /**
20783          * @event checkchange
20784          * Fires after the checked value has been set
20785          * @param {Roo.menu.CheckItem} this
20786          * @param {Boolean} checked The checked value that was set
20787          */
20788         "checkchange" : true
20789     });
20790     if(this.checkHandler){
20791         this.on('checkchange', this.checkHandler, this.scope);
20792     }
20793 };
20794 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20795     /**
20796      * @cfg {String} group
20797      * All check items with the same group name will automatically be grouped into a single-select
20798      * radio button group (defaults to '')
20799      */
20800     /**
20801      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20802      */
20803     itemCls : "x-menu-item x-menu-check-item",
20804     /**
20805      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20806      */
20807     groupClass : "x-menu-group-item",
20808
20809     /**
20810      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20811      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20812      * initialized with checked = true will be rendered as checked.
20813      */
20814     checked: false,
20815
20816     // private
20817     ctype: "Roo.menu.CheckItem",
20818
20819     // private
20820     onRender : function(c){
20821         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20822         if(this.group){
20823             this.el.addClass(this.groupClass);
20824         }
20825         Roo.menu.MenuMgr.registerCheckable(this);
20826         if(this.checked){
20827             this.checked = false;
20828             this.setChecked(true, true);
20829         }
20830     },
20831
20832     // private
20833     destroy : function(){
20834         if(this.rendered){
20835             Roo.menu.MenuMgr.unregisterCheckable(this);
20836         }
20837         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20838     },
20839
20840     /**
20841      * Set the checked state of this item
20842      * @param {Boolean} checked The new checked value
20843      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20844      */
20845     setChecked : function(state, suppressEvent){
20846         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20847             if(this.container){
20848                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20849             }
20850             this.checked = state;
20851             if(suppressEvent !== true){
20852                 this.fireEvent("checkchange", this, state);
20853             }
20854         }
20855     },
20856
20857     // private
20858     handleClick : function(e){
20859        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20860            this.setChecked(!this.checked);
20861        }
20862        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20863     }
20864 });/*
20865  * Based on:
20866  * Ext JS Library 1.1.1
20867  * Copyright(c) 2006-2007, Ext JS, LLC.
20868  *
20869  * Originally Released Under LGPL - original licence link has changed is not relivant.
20870  *
20871  * Fork - LGPL
20872  * <script type="text/javascript">
20873  */
20874  
20875 /**
20876  * @class Roo.menu.DateItem
20877  * @extends Roo.menu.Adapter
20878  * A menu item that wraps the {@link Roo.DatPicker} component.
20879  * @constructor
20880  * Creates a new DateItem
20881  * @param {Object} config Configuration options
20882  */
20883 Roo.menu.DateItem = function(config){
20884     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20885     /** The Roo.DatePicker object @type Roo.DatePicker */
20886     this.picker = this.component;
20887     this.addEvents({select: true});
20888     
20889     this.picker.on("render", function(picker){
20890         picker.getEl().swallowEvent("click");
20891         picker.container.addClass("x-menu-date-item");
20892     });
20893
20894     this.picker.on("select", this.onSelect, this);
20895 };
20896
20897 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20898     // private
20899     onSelect : function(picker, date){
20900         this.fireEvent("select", this, date, picker);
20901         Roo.menu.DateItem.superclass.handleClick.call(this);
20902     }
20903 });/*
20904  * Based on:
20905  * Ext JS Library 1.1.1
20906  * Copyright(c) 2006-2007, Ext JS, LLC.
20907  *
20908  * Originally Released Under LGPL - original licence link has changed is not relivant.
20909  *
20910  * Fork - LGPL
20911  * <script type="text/javascript">
20912  */
20913  
20914 /**
20915  * @class Roo.menu.ColorItem
20916  * @extends Roo.menu.Adapter
20917  * A menu item that wraps the {@link Roo.ColorPalette} component.
20918  * @constructor
20919  * Creates a new ColorItem
20920  * @param {Object} config Configuration options
20921  */
20922 Roo.menu.ColorItem = function(config){
20923     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20924     /** The Roo.ColorPalette object @type Roo.ColorPalette */
20925     this.palette = this.component;
20926     this.relayEvents(this.palette, ["select"]);
20927     if(this.selectHandler){
20928         this.on('select', this.selectHandler, this.scope);
20929     }
20930 };
20931 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
20932  * Based on:
20933  * Ext JS Library 1.1.1
20934  * Copyright(c) 2006-2007, Ext JS, LLC.
20935  *
20936  * Originally Released Under LGPL - original licence link has changed is not relivant.
20937  *
20938  * Fork - LGPL
20939  * <script type="text/javascript">
20940  */
20941  
20942
20943 /**
20944  * @class Roo.menu.DateMenu
20945  * @extends Roo.menu.Menu
20946  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20947  * @constructor
20948  * Creates a new DateMenu
20949  * @param {Object} config Configuration options
20950  */
20951 Roo.menu.DateMenu = function(config){
20952     Roo.menu.DateMenu.superclass.constructor.call(this, config);
20953     this.plain = true;
20954     var di = new Roo.menu.DateItem(config);
20955     this.add(di);
20956     /**
20957      * The {@link Roo.DatePicker} instance for this DateMenu
20958      * @type DatePicker
20959      */
20960     this.picker = di.picker;
20961     /**
20962      * @event select
20963      * @param {DatePicker} picker
20964      * @param {Date} date
20965      */
20966     this.relayEvents(di, ["select"]);
20967
20968     this.on('beforeshow', function(){
20969         if(this.picker){
20970             this.picker.hideMonthPicker(true);
20971         }
20972     }, this);
20973 };
20974 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
20975     cls:'x-date-menu'
20976 });/*
20977  * Based on:
20978  * Ext JS Library 1.1.1
20979  * Copyright(c) 2006-2007, Ext JS, LLC.
20980  *
20981  * Originally Released Under LGPL - original licence link has changed is not relivant.
20982  *
20983  * Fork - LGPL
20984  * <script type="text/javascript">
20985  */
20986  
20987
20988 /**
20989  * @class Roo.menu.ColorMenu
20990  * @extends Roo.menu.Menu
20991  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
20992  * @constructor
20993  * Creates a new ColorMenu
20994  * @param {Object} config Configuration options
20995  */
20996 Roo.menu.ColorMenu = function(config){
20997     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
20998     this.plain = true;
20999     var ci = new Roo.menu.ColorItem(config);
21000     this.add(ci);
21001     /**
21002      * The {@link Roo.ColorPalette} instance for this ColorMenu
21003      * @type ColorPalette
21004      */
21005     this.palette = ci.palette;
21006     /**
21007      * @event select
21008      * @param {ColorPalette} palette
21009      * @param {String} color
21010      */
21011     this.relayEvents(ci, ["select"]);
21012 };
21013 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
21014  * Based on:
21015  * Ext JS Library 1.1.1
21016  * Copyright(c) 2006-2007, Ext JS, LLC.
21017  *
21018  * Originally Released Under LGPL - original licence link has changed is not relivant.
21019  *
21020  * Fork - LGPL
21021  * <script type="text/javascript">
21022  */
21023  
21024 /**
21025  * @class Roo.form.Field
21026  * @extends Roo.BoxComponent
21027  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
21028  * @constructor
21029  * Creates a new Field
21030  * @param {Object} config Configuration options
21031  */
21032 Roo.form.Field = function(config){
21033     Roo.form.Field.superclass.constructor.call(this, config);
21034 };
21035
21036 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
21037     /**
21038      * @cfg {String} fieldLabel Label to use when rendering a form.
21039      */
21040        /**
21041      * @cfg {String} qtip Mouse over tip
21042      */
21043      
21044     /**
21045      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
21046      */
21047     invalidClass : "x-form-invalid",
21048     /**
21049      * @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")
21050      */
21051     invalidText : "The value in this field is invalid",
21052     /**
21053      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
21054      */
21055     focusClass : "x-form-focus",
21056     /**
21057      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
21058       automatic validation (defaults to "keyup").
21059      */
21060     validationEvent : "keyup",
21061     /**
21062      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
21063      */
21064     validateOnBlur : true,
21065     /**
21066      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
21067      */
21068     validationDelay : 250,
21069     /**
21070      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21071      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
21072      */
21073     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
21074     /**
21075      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
21076      */
21077     fieldClass : "x-form-field",
21078     /**
21079      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
21080      *<pre>
21081 Value         Description
21082 -----------   ----------------------------------------------------------------------
21083 qtip          Display a quick tip when the user hovers over the field
21084 title         Display a default browser title attribute popup
21085 under         Add a block div beneath the field containing the error text
21086 side          Add an error icon to the right of the field with a popup on hover
21087 [element id]  Add the error text directly to the innerHTML of the specified element
21088 </pre>
21089      */
21090     msgTarget : 'qtip',
21091     /**
21092      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
21093      */
21094     msgFx : 'normal',
21095
21096     /**
21097      * @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.
21098      */
21099     readOnly : false,
21100
21101     /**
21102      * @cfg {Boolean} disabled True to disable the field (defaults to false).
21103      */
21104     disabled : false,
21105
21106     /**
21107      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
21108      */
21109     inputType : undefined,
21110     
21111     /**
21112      * @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).
21113          */
21114         tabIndex : undefined,
21115         
21116     // private
21117     isFormField : true,
21118
21119     // private
21120     hasFocus : false,
21121     /**
21122      * @property {Roo.Element} fieldEl
21123      * Element Containing the rendered Field (with label etc.)
21124      */
21125     /**
21126      * @cfg {Mixed} value A value to initialize this field with.
21127      */
21128     value : undefined,
21129
21130     /**
21131      * @cfg {String} name The field's HTML name attribute.
21132      */
21133     /**
21134      * @cfg {String} cls A CSS class to apply to the field's underlying element.
21135      */
21136
21137         // private ??
21138         initComponent : function(){
21139         Roo.form.Field.superclass.initComponent.call(this);
21140         this.addEvents({
21141             /**
21142              * @event focus
21143              * Fires when this field receives input focus.
21144              * @param {Roo.form.Field} this
21145              */
21146             focus : true,
21147             /**
21148              * @event blur
21149              * Fires when this field loses input focus.
21150              * @param {Roo.form.Field} this
21151              */
21152             blur : true,
21153             /**
21154              * @event specialkey
21155              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
21156              * {@link Roo.EventObject#getKey} to determine which key was pressed.
21157              * @param {Roo.form.Field} this
21158              * @param {Roo.EventObject} e The event object
21159              */
21160             specialkey : true,
21161             /**
21162              * @event change
21163              * Fires just before the field blurs if the field value has changed.
21164              * @param {Roo.form.Field} this
21165              * @param {Mixed} newValue The new value
21166              * @param {Mixed} oldValue The original value
21167              */
21168             change : true,
21169             /**
21170              * @event invalid
21171              * Fires after the field has been marked as invalid.
21172              * @param {Roo.form.Field} this
21173              * @param {String} msg The validation message
21174              */
21175             invalid : true,
21176             /**
21177              * @event valid
21178              * Fires after the field has been validated with no errors.
21179              * @param {Roo.form.Field} this
21180              */
21181             valid : true,
21182              /**
21183              * @event keyup
21184              * Fires after the key up
21185              * @param {Roo.form.Field} this
21186              * @param {Roo.EventObject}  e The event Object
21187              */
21188             keyup : true
21189         });
21190     },
21191
21192     /**
21193      * Returns the name attribute of the field if available
21194      * @return {String} name The field name
21195      */
21196     getName: function(){
21197          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
21198     },
21199
21200     // private
21201     onRender : function(ct, position){
21202         Roo.form.Field.superclass.onRender.call(this, ct, position);
21203         if(!this.el){
21204             var cfg = this.getAutoCreate();
21205             if(!cfg.name){
21206                 cfg.name = this.name || this.id;
21207             }
21208             if(this.inputType){
21209                 cfg.type = this.inputType;
21210             }
21211             this.el = ct.createChild(cfg, position);
21212         }
21213         var type = this.el.dom.type;
21214         if(type){
21215             if(type == 'password'){
21216                 type = 'text';
21217             }
21218             this.el.addClass('x-form-'+type);
21219         }
21220         if(this.readOnly){
21221             this.el.dom.readOnly = true;
21222         }
21223         if(this.tabIndex !== undefined){
21224             this.el.dom.setAttribute('tabIndex', this.tabIndex);
21225         }
21226
21227         this.el.addClass([this.fieldClass, this.cls]);
21228         this.initValue();
21229     },
21230
21231     /**
21232      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
21233      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
21234      * @return {Roo.form.Field} this
21235      */
21236     applyTo : function(target){
21237         this.allowDomMove = false;
21238         this.el = Roo.get(target);
21239         this.render(this.el.dom.parentNode);
21240         return this;
21241     },
21242
21243     // private
21244     initValue : function(){
21245         if(this.value !== undefined){
21246             this.setValue(this.value);
21247         }else if(this.el.dom.value.length > 0){
21248             this.setValue(this.el.dom.value);
21249         }
21250     },
21251
21252     /**
21253      * Returns true if this field has been changed since it was originally loaded and is not disabled.
21254      */
21255     isDirty : function() {
21256         if(this.disabled) {
21257             return false;
21258         }
21259         return String(this.getValue()) !== String(this.originalValue);
21260     },
21261
21262     // private
21263     afterRender : function(){
21264         Roo.form.Field.superclass.afterRender.call(this);
21265         this.initEvents();
21266     },
21267
21268     // private
21269     fireKey : function(e){
21270         //Roo.log('field ' + e.getKey());
21271         if(e.isNavKeyPress()){
21272             this.fireEvent("specialkey", this, e);
21273         }
21274     },
21275
21276     /**
21277      * Resets the current field value to the originally loaded value and clears any validation messages
21278      */
21279     reset : function(){
21280         this.setValue(this.originalValue);
21281         this.clearInvalid();
21282     },
21283
21284     // private
21285     initEvents : function(){
21286         // safari killled keypress - so keydown is now used..
21287         this.el.on("keydown" , this.fireKey,  this);
21288         this.el.on("focus", this.onFocus,  this);
21289         this.el.on("blur", this.onBlur,  this);
21290         this.el.relayEvent('keyup', this);
21291
21292         // reference to original value for reset
21293         this.originalValue = this.getValue();
21294     },
21295
21296     // private
21297     onFocus : function(){
21298         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21299             this.el.addClass(this.focusClass);
21300         }
21301         if(!this.hasFocus){
21302             this.hasFocus = true;
21303             this.startValue = this.getValue();
21304             this.fireEvent("focus", this);
21305         }
21306     },
21307
21308     beforeBlur : Roo.emptyFn,
21309
21310     // private
21311     onBlur : function(){
21312         this.beforeBlur();
21313         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21314             this.el.removeClass(this.focusClass);
21315         }
21316         this.hasFocus = false;
21317         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21318             this.validate();
21319         }
21320         var v = this.getValue();
21321         if(String(v) !== String(this.startValue)){
21322             this.fireEvent('change', this, v, this.startValue);
21323         }
21324         this.fireEvent("blur", this);
21325     },
21326
21327     /**
21328      * Returns whether or not the field value is currently valid
21329      * @param {Boolean} preventMark True to disable marking the field invalid
21330      * @return {Boolean} True if the value is valid, else false
21331      */
21332     isValid : function(preventMark){
21333         if(this.disabled){
21334             return true;
21335         }
21336         var restore = this.preventMark;
21337         this.preventMark = preventMark === true;
21338         var v = this.validateValue(this.processValue(this.getRawValue()));
21339         this.preventMark = restore;
21340         return v;
21341     },
21342
21343     /**
21344      * Validates the field value
21345      * @return {Boolean} True if the value is valid, else false
21346      */
21347     validate : function(){
21348         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21349             this.clearInvalid();
21350             return true;
21351         }
21352         return false;
21353     },
21354
21355     processValue : function(value){
21356         return value;
21357     },
21358
21359     // private
21360     // Subclasses should provide the validation implementation by overriding this
21361     validateValue : function(value){
21362         return true;
21363     },
21364
21365     /**
21366      * Mark this field as invalid
21367      * @param {String} msg The validation message
21368      */
21369     markInvalid : function(msg){
21370         if(!this.rendered || this.preventMark){ // not rendered
21371             return;
21372         }
21373         this.el.addClass(this.invalidClass);
21374         msg = msg || this.invalidText;
21375         switch(this.msgTarget){
21376             case 'qtip':
21377                 this.el.dom.qtip = msg;
21378                 this.el.dom.qclass = 'x-form-invalid-tip';
21379                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21380                     Roo.QuickTips.enable();
21381                 }
21382                 break;
21383             case 'title':
21384                 this.el.dom.title = msg;
21385                 break;
21386             case 'under':
21387                 if(!this.errorEl){
21388                     var elp = this.el.findParent('.x-form-element', 5, true);
21389                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21390                     this.errorEl.setWidth(elp.getWidth(true)-20);
21391                 }
21392                 this.errorEl.update(msg);
21393                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21394                 break;
21395             case 'side':
21396                 if(!this.errorIcon){
21397                     var elp = this.el.findParent('.x-form-element', 5, true);
21398                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21399                 }
21400                 this.alignErrorIcon();
21401                 this.errorIcon.dom.qtip = msg;
21402                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21403                 this.errorIcon.show();
21404                 this.on('resize', this.alignErrorIcon, this);
21405                 break;
21406             default:
21407                 var t = Roo.getDom(this.msgTarget);
21408                 t.innerHTML = msg;
21409                 t.style.display = this.msgDisplay;
21410                 break;
21411         }
21412         this.fireEvent('invalid', this, msg);
21413     },
21414
21415     // private
21416     alignErrorIcon : function(){
21417         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21418     },
21419
21420     /**
21421      * Clear any invalid styles/messages for this field
21422      */
21423     clearInvalid : function(){
21424         if(!this.rendered || this.preventMark){ // not rendered
21425             return;
21426         }
21427         this.el.removeClass(this.invalidClass);
21428         switch(this.msgTarget){
21429             case 'qtip':
21430                 this.el.dom.qtip = '';
21431                 break;
21432             case 'title':
21433                 this.el.dom.title = '';
21434                 break;
21435             case 'under':
21436                 if(this.errorEl){
21437                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21438                 }
21439                 break;
21440             case 'side':
21441                 if(this.errorIcon){
21442                     this.errorIcon.dom.qtip = '';
21443                     this.errorIcon.hide();
21444                     this.un('resize', this.alignErrorIcon, this);
21445                 }
21446                 break;
21447             default:
21448                 var t = Roo.getDom(this.msgTarget);
21449                 t.innerHTML = '';
21450                 t.style.display = 'none';
21451                 break;
21452         }
21453         this.fireEvent('valid', this);
21454     },
21455
21456     /**
21457      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21458      * @return {Mixed} value The field value
21459      */
21460     getRawValue : function(){
21461         var v = this.el.getValue();
21462         if(v === this.emptyText){
21463             v = '';
21464         }
21465         return v;
21466     },
21467
21468     /**
21469      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21470      * @return {Mixed} value The field value
21471      */
21472     getValue : function(){
21473         var v = this.el.getValue();
21474         if(v === this.emptyText || v === undefined){
21475             v = '';
21476         }
21477         return v;
21478     },
21479
21480     /**
21481      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21482      * @param {Mixed} value The value to set
21483      */
21484     setRawValue : function(v){
21485         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21486     },
21487
21488     /**
21489      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21490      * @param {Mixed} value The value to set
21491      */
21492     setValue : function(v){
21493         this.value = v;
21494         if(this.rendered){
21495             this.el.dom.value = (v === null || v === undefined ? '' : v);
21496              this.validate();
21497         }
21498     },
21499
21500     adjustSize : function(w, h){
21501         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21502         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21503         return s;
21504     },
21505
21506     adjustWidth : function(tag, w){
21507         tag = tag.toLowerCase();
21508         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21509             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21510                 if(tag == 'input'){
21511                     return w + 2;
21512                 }
21513                 if(tag = 'textarea'){
21514                     return w-2;
21515                 }
21516             }else if(Roo.isOpera){
21517                 if(tag == 'input'){
21518                     return w + 2;
21519                 }
21520                 if(tag = 'textarea'){
21521                     return w-2;
21522                 }
21523             }
21524         }
21525         return w;
21526     }
21527 });
21528
21529
21530 // anything other than normal should be considered experimental
21531 Roo.form.Field.msgFx = {
21532     normal : {
21533         show: function(msgEl, f){
21534             msgEl.setDisplayed('block');
21535         },
21536
21537         hide : function(msgEl, f){
21538             msgEl.setDisplayed(false).update('');
21539         }
21540     },
21541
21542     slide : {
21543         show: function(msgEl, f){
21544             msgEl.slideIn('t', {stopFx:true});
21545         },
21546
21547         hide : function(msgEl, f){
21548             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21549         }
21550     },
21551
21552     slideRight : {
21553         show: function(msgEl, f){
21554             msgEl.fixDisplay();
21555             msgEl.alignTo(f.el, 'tl-tr');
21556             msgEl.slideIn('l', {stopFx:true});
21557         },
21558
21559         hide : function(msgEl, f){
21560             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21561         }
21562     }
21563 };/*
21564  * Based on:
21565  * Ext JS Library 1.1.1
21566  * Copyright(c) 2006-2007, Ext JS, LLC.
21567  *
21568  * Originally Released Under LGPL - original licence link has changed is not relivant.
21569  *
21570  * Fork - LGPL
21571  * <script type="text/javascript">
21572  */
21573  
21574
21575 /**
21576  * @class Roo.form.TextField
21577  * @extends Roo.form.Field
21578  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21579  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21580  * @constructor
21581  * Creates a new TextField
21582  * @param {Object} config Configuration options
21583  */
21584 Roo.form.TextField = function(config){
21585     Roo.form.TextField.superclass.constructor.call(this, config);
21586     this.addEvents({
21587         /**
21588          * @event autosize
21589          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21590          * according to the default logic, but this event provides a hook for the developer to apply additional
21591          * logic at runtime to resize the field if needed.
21592              * @param {Roo.form.Field} this This text field
21593              * @param {Number} width The new field width
21594              */
21595         autosize : true
21596     });
21597 };
21598
21599 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21600     /**
21601      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21602      */
21603     grow : false,
21604     /**
21605      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21606      */
21607     growMin : 30,
21608     /**
21609      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21610      */
21611     growMax : 800,
21612     /**
21613      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21614      */
21615     vtype : null,
21616     /**
21617      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21618      */
21619     maskRe : null,
21620     /**
21621      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21622      */
21623     disableKeyFilter : false,
21624     /**
21625      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21626      */
21627     allowBlank : true,
21628     /**
21629      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21630      */
21631     minLength : 0,
21632     /**
21633      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21634      */
21635     maxLength : Number.MAX_VALUE,
21636     /**
21637      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21638      */
21639     minLengthText : "The minimum length for this field is {0}",
21640     /**
21641      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21642      */
21643     maxLengthText : "The maximum length for this field is {0}",
21644     /**
21645      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21646      */
21647     selectOnFocus : false,
21648     /**
21649      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21650      */
21651     blankText : "This field is required",
21652     /**
21653      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21654      * If available, this function will be called only after the basic validators all return true, and will be passed the
21655      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21656      */
21657     validator : null,
21658     /**
21659      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21660      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21661      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21662      */
21663     regex : null,
21664     /**
21665      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21666      */
21667     regexText : "",
21668     /**
21669      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
21670      */
21671     emptyText : null,
21672     /**
21673      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
21674      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
21675      */
21676     emptyClass : 'x-form-empty-field',
21677
21678     // private
21679     initEvents : function(){
21680         Roo.form.TextField.superclass.initEvents.call(this);
21681         if(this.validationEvent == 'keyup'){
21682             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21683             this.el.on('keyup', this.filterValidation, this);
21684         }
21685         else if(this.validationEvent !== false){
21686             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21687         }
21688         if(this.selectOnFocus || this.emptyText){
21689             this.on("focus", this.preFocus, this);
21690             if(this.emptyText){
21691                 this.on('blur', this.postBlur, this);
21692                 this.applyEmptyText();
21693             }
21694         }
21695         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21696             this.el.on("keypress", this.filterKeys, this);
21697         }
21698         if(this.grow){
21699             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21700             this.el.on("click", this.autoSize,  this);
21701         }
21702     },
21703
21704     processValue : function(value){
21705         if(this.stripCharsRe){
21706             var newValue = value.replace(this.stripCharsRe, '');
21707             if(newValue !== value){
21708                 this.setRawValue(newValue);
21709                 return newValue;
21710             }
21711         }
21712         return value;
21713     },
21714
21715     filterValidation : function(e){
21716         if(!e.isNavKeyPress()){
21717             this.validationTask.delay(this.validationDelay);
21718         }
21719     },
21720
21721     // private
21722     onKeyUp : function(e){
21723         if(!e.isNavKeyPress()){
21724             this.autoSize();
21725         }
21726     },
21727
21728     /**
21729      * Resets the current field value to the originally-loaded value and clears any validation messages.
21730      * Also adds emptyText and emptyClass if the original value was blank.
21731      */
21732     reset : function(){
21733         Roo.form.TextField.superclass.reset.call(this);
21734         this.applyEmptyText();
21735     },
21736
21737     applyEmptyText : function(){
21738         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
21739             this.setRawValue(this.emptyText);
21740             this.el.addClass(this.emptyClass);
21741         }
21742     },
21743
21744     // private
21745     preFocus : function(){
21746         if(this.emptyText){
21747             if(this.el.dom.value == this.emptyText){
21748                 this.setRawValue('');
21749             }
21750             this.el.removeClass(this.emptyClass);
21751         }
21752         if(this.selectOnFocus){
21753             this.el.dom.select();
21754         }
21755     },
21756
21757     // private
21758     postBlur : function(){
21759         this.applyEmptyText();
21760     },
21761
21762     // private
21763     filterKeys : function(e){
21764         var k = e.getKey();
21765         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21766             return;
21767         }
21768         var c = e.getCharCode(), cc = String.fromCharCode(c);
21769         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21770             return;
21771         }
21772         if(!this.maskRe.test(cc)){
21773             e.stopEvent();
21774         }
21775     },
21776
21777     setValue : function(v){
21778         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
21779             this.el.removeClass(this.emptyClass);
21780         }
21781         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21782         this.applyEmptyText();
21783         this.autoSize();
21784     },
21785
21786     /**
21787      * Validates a value according to the field's validation rules and marks the field as invalid
21788      * if the validation fails
21789      * @param {Mixed} value The value to validate
21790      * @return {Boolean} True if the value is valid, else false
21791      */
21792     validateValue : function(value){
21793         if(value.length < 1 || value === this.emptyText){ // if it's blank
21794              if(this.allowBlank){
21795                 this.clearInvalid();
21796                 return true;
21797              }else{
21798                 this.markInvalid(this.blankText);
21799                 return false;
21800              }
21801         }
21802         if(value.length < this.minLength){
21803             this.markInvalid(String.format(this.minLengthText, this.minLength));
21804             return false;
21805         }
21806         if(value.length > this.maxLength){
21807             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21808             return false;
21809         }
21810         if(this.vtype){
21811             var vt = Roo.form.VTypes;
21812             if(!vt[this.vtype](value, this)){
21813                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21814                 return false;
21815             }
21816         }
21817         if(typeof this.validator == "function"){
21818             var msg = this.validator(value);
21819             if(msg !== true){
21820                 this.markInvalid(msg);
21821                 return false;
21822             }
21823         }
21824         if(this.regex && !this.regex.test(value)){
21825             this.markInvalid(this.regexText);
21826             return false;
21827         }
21828         return true;
21829     },
21830
21831     /**
21832      * Selects text in this field
21833      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21834      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21835      */
21836     selectText : function(start, end){
21837         var v = this.getRawValue();
21838         if(v.length > 0){
21839             start = start === undefined ? 0 : start;
21840             end = end === undefined ? v.length : end;
21841             var d = this.el.dom;
21842             if(d.setSelectionRange){
21843                 d.setSelectionRange(start, end);
21844             }else if(d.createTextRange){
21845                 var range = d.createTextRange();
21846                 range.moveStart("character", start);
21847                 range.moveEnd("character", v.length-end);
21848                 range.select();
21849             }
21850         }
21851     },
21852
21853     /**
21854      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21855      * This only takes effect if grow = true, and fires the autosize event.
21856      */
21857     autoSize : function(){
21858         if(!this.grow || !this.rendered){
21859             return;
21860         }
21861         if(!this.metrics){
21862             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21863         }
21864         var el = this.el;
21865         var v = el.dom.value;
21866         var d = document.createElement('div');
21867         d.appendChild(document.createTextNode(v));
21868         v = d.innerHTML;
21869         d = null;
21870         v += "&#160;";
21871         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21872         this.el.setWidth(w);
21873         this.fireEvent("autosize", this, w);
21874     }
21875 });/*
21876  * Based on:
21877  * Ext JS Library 1.1.1
21878  * Copyright(c) 2006-2007, Ext JS, LLC.
21879  *
21880  * Originally Released Under LGPL - original licence link has changed is not relivant.
21881  *
21882  * Fork - LGPL
21883  * <script type="text/javascript">
21884  */
21885  
21886 /**
21887  * @class Roo.form.Hidden
21888  * @extends Roo.form.TextField
21889  * Simple Hidden element used on forms 
21890  * 
21891  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21892  * 
21893  * @constructor
21894  * Creates a new Hidden form element.
21895  * @param {Object} config Configuration options
21896  */
21897
21898
21899
21900 // easy hidden field...
21901 Roo.form.Hidden = function(config){
21902     Roo.form.Hidden.superclass.constructor.call(this, config);
21903 };
21904   
21905 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21906     fieldLabel:      '',
21907     inputType:      'hidden',
21908     width:          50,
21909     allowBlank:     true,
21910     labelSeparator: '',
21911     hidden:         true,
21912     itemCls :       'x-form-item-display-none'
21913
21914
21915 });
21916
21917
21918 /*
21919  * Based on:
21920  * Ext JS Library 1.1.1
21921  * Copyright(c) 2006-2007, Ext JS, LLC.
21922  *
21923  * Originally Released Under LGPL - original licence link has changed is not relivant.
21924  *
21925  * Fork - LGPL
21926  * <script type="text/javascript">
21927  */
21928  
21929 /**
21930  * @class Roo.form.TriggerField
21931  * @extends Roo.form.TextField
21932  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
21933  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
21934  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
21935  * for which you can provide a custom implementation.  For example:
21936  * <pre><code>
21937 var trigger = new Roo.form.TriggerField();
21938 trigger.onTriggerClick = myTriggerFn;
21939 trigger.applyTo('my-field');
21940 </code></pre>
21941  *
21942  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
21943  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
21944  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
21945  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
21946  * @constructor
21947  * Create a new TriggerField.
21948  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
21949  * to the base TextField)
21950  */
21951 Roo.form.TriggerField = function(config){
21952     this.mimicing = false;
21953     Roo.form.TriggerField.superclass.constructor.call(this, config);
21954 };
21955
21956 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
21957     /**
21958      * @cfg {String} triggerClass A CSS class to apply to the trigger
21959      */
21960     /**
21961      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21962      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
21963      */
21964     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
21965     /**
21966      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
21967      */
21968     hideTrigger:false,
21969
21970     /** @cfg {Boolean} grow @hide */
21971     /** @cfg {Number} growMin @hide */
21972     /** @cfg {Number} growMax @hide */
21973
21974     /**
21975      * @hide 
21976      * @method
21977      */
21978     autoSize: Roo.emptyFn,
21979     // private
21980     monitorTab : true,
21981     // private
21982     deferHeight : true,
21983
21984     
21985     actionMode : 'wrap',
21986     // private
21987     onResize : function(w, h){
21988         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
21989         if(typeof w == 'number'){
21990             var x = w - this.trigger.getWidth();
21991             this.el.setWidth(this.adjustWidth('input', x));
21992             this.trigger.setStyle('left', x+'px');
21993         }
21994     },
21995
21996     // private
21997     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21998
21999     // private
22000     getResizeEl : function(){
22001         return this.wrap;
22002     },
22003
22004     // private
22005     getPositionEl : function(){
22006         return this.wrap;
22007     },
22008
22009     // private
22010     alignErrorIcon : function(){
22011         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
22012     },
22013
22014     // private
22015     onRender : function(ct, position){
22016         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
22017         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
22018         this.trigger = this.wrap.createChild(this.triggerConfig ||
22019                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
22020         if(this.hideTrigger){
22021             this.trigger.setDisplayed(false);
22022         }
22023         this.initTrigger();
22024         if(!this.width){
22025             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
22026         }
22027     },
22028
22029     // private
22030     initTrigger : function(){
22031         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
22032         this.trigger.addClassOnOver('x-form-trigger-over');
22033         this.trigger.addClassOnClick('x-form-trigger-click');
22034     },
22035
22036     // private
22037     onDestroy : function(){
22038         if(this.trigger){
22039             this.trigger.removeAllListeners();
22040             this.trigger.remove();
22041         }
22042         if(this.wrap){
22043             this.wrap.remove();
22044         }
22045         Roo.form.TriggerField.superclass.onDestroy.call(this);
22046     },
22047
22048     // private
22049     onFocus : function(){
22050         Roo.form.TriggerField.superclass.onFocus.call(this);
22051         if(!this.mimicing){
22052             this.wrap.addClass('x-trigger-wrap-focus');
22053             this.mimicing = true;
22054             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
22055             if(this.monitorTab){
22056                 this.el.on("keydown", this.checkTab, this);
22057             }
22058         }
22059     },
22060
22061     // private
22062     checkTab : function(e){
22063         if(e.getKey() == e.TAB){
22064             this.triggerBlur();
22065         }
22066     },
22067
22068     // private
22069     onBlur : function(){
22070         // do nothing
22071     },
22072
22073     // private
22074     mimicBlur : function(e, t){
22075         if(!this.wrap.contains(t) && this.validateBlur()){
22076             this.triggerBlur();
22077         }
22078     },
22079
22080     // private
22081     triggerBlur : function(){
22082         this.mimicing = false;
22083         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
22084         if(this.monitorTab){
22085             this.el.un("keydown", this.checkTab, this);
22086         }
22087         this.wrap.removeClass('x-trigger-wrap-focus');
22088         Roo.form.TriggerField.superclass.onBlur.call(this);
22089     },
22090
22091     // private
22092     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
22093     validateBlur : function(e, t){
22094         return true;
22095     },
22096
22097     // private
22098     onDisable : function(){
22099         Roo.form.TriggerField.superclass.onDisable.call(this);
22100         if(this.wrap){
22101             this.wrap.addClass('x-item-disabled');
22102         }
22103     },
22104
22105     // private
22106     onEnable : function(){
22107         Roo.form.TriggerField.superclass.onEnable.call(this);
22108         if(this.wrap){
22109             this.wrap.removeClass('x-item-disabled');
22110         }
22111     },
22112
22113     // private
22114     onShow : function(){
22115         var ae = this.getActionEl();
22116         
22117         if(ae){
22118             ae.dom.style.display = '';
22119             ae.dom.style.visibility = 'visible';
22120         }
22121     },
22122
22123     // private
22124     
22125     onHide : function(){
22126         var ae = this.getActionEl();
22127         ae.dom.style.display = 'none';
22128     },
22129
22130     /**
22131      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
22132      * by an implementing function.
22133      * @method
22134      * @param {EventObject} e
22135      */
22136     onTriggerClick : Roo.emptyFn
22137 });
22138
22139 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
22140 // to be extended by an implementing class.  For an example of implementing this class, see the custom
22141 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
22142 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
22143     initComponent : function(){
22144         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
22145
22146         this.triggerConfig = {
22147             tag:'span', cls:'x-form-twin-triggers', cn:[
22148             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
22149             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
22150         ]};
22151     },
22152
22153     getTrigger : function(index){
22154         return this.triggers[index];
22155     },
22156
22157     initTrigger : function(){
22158         var ts = this.trigger.select('.x-form-trigger', true);
22159         this.wrap.setStyle('overflow', 'hidden');
22160         var triggerField = this;
22161         ts.each(function(t, all, index){
22162             t.hide = function(){
22163                 var w = triggerField.wrap.getWidth();
22164                 this.dom.style.display = 'none';
22165                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22166             };
22167             t.show = function(){
22168                 var w = triggerField.wrap.getWidth();
22169                 this.dom.style.display = '';
22170                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22171             };
22172             var triggerIndex = 'Trigger'+(index+1);
22173
22174             if(this['hide'+triggerIndex]){
22175                 t.dom.style.display = 'none';
22176             }
22177             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
22178             t.addClassOnOver('x-form-trigger-over');
22179             t.addClassOnClick('x-form-trigger-click');
22180         }, this);
22181         this.triggers = ts.elements;
22182     },
22183
22184     onTrigger1Click : Roo.emptyFn,
22185     onTrigger2Click : Roo.emptyFn
22186 });/*
22187  * Based on:
22188  * Ext JS Library 1.1.1
22189  * Copyright(c) 2006-2007, Ext JS, LLC.
22190  *
22191  * Originally Released Under LGPL - original licence link has changed is not relivant.
22192  *
22193  * Fork - LGPL
22194  * <script type="text/javascript">
22195  */
22196  
22197 /**
22198  * @class Roo.form.TextArea
22199  * @extends Roo.form.TextField
22200  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
22201  * support for auto-sizing.
22202  * @constructor
22203  * Creates a new TextArea
22204  * @param {Object} config Configuration options
22205  */
22206 Roo.form.TextArea = function(config){
22207     Roo.form.TextArea.superclass.constructor.call(this, config);
22208     // these are provided exchanges for backwards compat
22209     // minHeight/maxHeight were replaced by growMin/growMax to be
22210     // compatible with TextField growing config values
22211     if(this.minHeight !== undefined){
22212         this.growMin = this.minHeight;
22213     }
22214     if(this.maxHeight !== undefined){
22215         this.growMax = this.maxHeight;
22216     }
22217 };
22218
22219 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
22220     /**
22221      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
22222      */
22223     growMin : 60,
22224     /**
22225      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
22226      */
22227     growMax: 1000,
22228     /**
22229      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
22230      * in the field (equivalent to setting overflow: hidden, defaults to false)
22231      */
22232     preventScrollbars: false,
22233     /**
22234      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22235      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
22236      */
22237
22238     // private
22239     onRender : function(ct, position){
22240         if(!this.el){
22241             this.defaultAutoCreate = {
22242                 tag: "textarea",
22243                 style:"width:300px;height:60px;",
22244                 autocomplete: "off"
22245             };
22246         }
22247         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
22248         if(this.grow){
22249             this.textSizeEl = Roo.DomHelper.append(document.body, {
22250                 tag: "pre", cls: "x-form-grow-sizer"
22251             });
22252             if(this.preventScrollbars){
22253                 this.el.setStyle("overflow", "hidden");
22254             }
22255             this.el.setHeight(this.growMin);
22256         }
22257     },
22258
22259     onDestroy : function(){
22260         if(this.textSizeEl){
22261             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
22262         }
22263         Roo.form.TextArea.superclass.onDestroy.call(this);
22264     },
22265
22266     // private
22267     onKeyUp : function(e){
22268         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
22269             this.autoSize();
22270         }
22271     },
22272
22273     /**
22274      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
22275      * This only takes effect if grow = true, and fires the autosize event if the height changes.
22276      */
22277     autoSize : function(){
22278         if(!this.grow || !this.textSizeEl){
22279             return;
22280         }
22281         var el = this.el;
22282         var v = el.dom.value;
22283         var ts = this.textSizeEl;
22284
22285         ts.innerHTML = '';
22286         ts.appendChild(document.createTextNode(v));
22287         v = ts.innerHTML;
22288
22289         Roo.fly(ts).setWidth(this.el.getWidth());
22290         if(v.length < 1){
22291             v = "&#160;&#160;";
22292         }else{
22293             if(Roo.isIE){
22294                 v = v.replace(/\n/g, '<p>&#160;</p>');
22295             }
22296             v += "&#160;\n&#160;";
22297         }
22298         ts.innerHTML = v;
22299         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
22300         if(h != this.lastHeight){
22301             this.lastHeight = h;
22302             this.el.setHeight(h);
22303             this.fireEvent("autosize", this, h);
22304         }
22305     }
22306 });/*
22307  * Based on:
22308  * Ext JS Library 1.1.1
22309  * Copyright(c) 2006-2007, Ext JS, LLC.
22310  *
22311  * Originally Released Under LGPL - original licence link has changed is not relivant.
22312  *
22313  * Fork - LGPL
22314  * <script type="text/javascript">
22315  */
22316  
22317
22318 /**
22319  * @class Roo.form.NumberField
22320  * @extends Roo.form.TextField
22321  * Numeric text field that provides automatic keystroke filtering and numeric validation.
22322  * @constructor
22323  * Creates a new NumberField
22324  * @param {Object} config Configuration options
22325  */
22326 Roo.form.NumberField = function(config){
22327     Roo.form.NumberField.superclass.constructor.call(this, config);
22328 };
22329
22330 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
22331     /**
22332      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22333      */
22334     fieldClass: "x-form-field x-form-num-field",
22335     /**
22336      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22337      */
22338     allowDecimals : true,
22339     /**
22340      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22341      */
22342     decimalSeparator : ".",
22343     /**
22344      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22345      */
22346     decimalPrecision : 2,
22347     /**
22348      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22349      */
22350     allowNegative : true,
22351     /**
22352      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22353      */
22354     minValue : Number.NEGATIVE_INFINITY,
22355     /**
22356      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22357      */
22358     maxValue : Number.MAX_VALUE,
22359     /**
22360      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22361      */
22362     minText : "The minimum value for this field is {0}",
22363     /**
22364      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22365      */
22366     maxText : "The maximum value for this field is {0}",
22367     /**
22368      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22369      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22370      */
22371     nanText : "{0} is not a valid number",
22372
22373     // private
22374     initEvents : function(){
22375         Roo.form.NumberField.superclass.initEvents.call(this);
22376         var allowed = "0123456789";
22377         if(this.allowDecimals){
22378             allowed += this.decimalSeparator;
22379         }
22380         if(this.allowNegative){
22381             allowed += "-";
22382         }
22383         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22384         var keyPress = function(e){
22385             var k = e.getKey();
22386             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22387                 return;
22388             }
22389             var c = e.getCharCode();
22390             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22391                 e.stopEvent();
22392             }
22393         };
22394         this.el.on("keypress", keyPress, this);
22395     },
22396
22397     // private
22398     validateValue : function(value){
22399         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22400             return false;
22401         }
22402         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22403              return true;
22404         }
22405         var num = this.parseValue(value);
22406         if(isNaN(num)){
22407             this.markInvalid(String.format(this.nanText, value));
22408             return false;
22409         }
22410         if(num < this.minValue){
22411             this.markInvalid(String.format(this.minText, this.minValue));
22412             return false;
22413         }
22414         if(num > this.maxValue){
22415             this.markInvalid(String.format(this.maxText, this.maxValue));
22416             return false;
22417         }
22418         return true;
22419     },
22420
22421     getValue : function(){
22422         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22423     },
22424
22425     // private
22426     parseValue : function(value){
22427         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22428         return isNaN(value) ? '' : value;
22429     },
22430
22431     // private
22432     fixPrecision : function(value){
22433         var nan = isNaN(value);
22434         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22435             return nan ? '' : value;
22436         }
22437         return parseFloat(value).toFixed(this.decimalPrecision);
22438     },
22439
22440     setValue : function(v){
22441         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22442     },
22443
22444     // private
22445     decimalPrecisionFcn : function(v){
22446         return Math.floor(v);
22447     },
22448
22449     beforeBlur : function(){
22450         var v = this.parseValue(this.getRawValue());
22451         if(v){
22452             this.setValue(this.fixPrecision(v));
22453         }
22454     }
22455 });/*
22456  * Based on:
22457  * Ext JS Library 1.1.1
22458  * Copyright(c) 2006-2007, Ext JS, LLC.
22459  *
22460  * Originally Released Under LGPL - original licence link has changed is not relivant.
22461  *
22462  * Fork - LGPL
22463  * <script type="text/javascript">
22464  */
22465  
22466 /**
22467  * @class Roo.form.DateField
22468  * @extends Roo.form.TriggerField
22469  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22470 * @constructor
22471 * Create a new DateField
22472 * @param {Object} config
22473  */
22474 Roo.form.DateField = function(config){
22475     Roo.form.DateField.superclass.constructor.call(this, config);
22476     
22477       this.addEvents({
22478          
22479         /**
22480          * @event select
22481          * Fires when a date is selected
22482              * @param {Roo.form.DateField} combo This combo box
22483              * @param {Date} date The date selected
22484              */
22485         'select' : true
22486          
22487     });
22488     
22489     
22490     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22491     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22492     this.ddMatch = null;
22493     if(this.disabledDates){
22494         var dd = this.disabledDates;
22495         var re = "(?:";
22496         for(var i = 0; i < dd.length; i++){
22497             re += dd[i];
22498             if(i != dd.length-1) re += "|";
22499         }
22500         this.ddMatch = new RegExp(re + ")");
22501     }
22502 };
22503
22504 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22505     /**
22506      * @cfg {String} format
22507      * The default date format string which can be overriden for localization support.  The format must be
22508      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22509      */
22510     format : "m/d/y",
22511     /**
22512      * @cfg {String} altFormats
22513      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22514      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22515      */
22516     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22517     /**
22518      * @cfg {Array} disabledDays
22519      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22520      */
22521     disabledDays : null,
22522     /**
22523      * @cfg {String} disabledDaysText
22524      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22525      */
22526     disabledDaysText : "Disabled",
22527     /**
22528      * @cfg {Array} disabledDates
22529      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22530      * expression so they are very powerful. Some examples:
22531      * <ul>
22532      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22533      * <li>["03/08", "09/16"] would disable those days for every year</li>
22534      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22535      * <li>["03/../2006"] would disable every day in March 2006</li>
22536      * <li>["^03"] would disable every day in every March</li>
22537      * </ul>
22538      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22539      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22540      */
22541     disabledDates : null,
22542     /**
22543      * @cfg {String} disabledDatesText
22544      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22545      */
22546     disabledDatesText : "Disabled",
22547     /**
22548      * @cfg {Date/String} minValue
22549      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22550      * valid format (defaults to null).
22551      */
22552     minValue : null,
22553     /**
22554      * @cfg {Date/String} maxValue
22555      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22556      * valid format (defaults to null).
22557      */
22558     maxValue : null,
22559     /**
22560      * @cfg {String} minText
22561      * The error text to display when the date in the cell is before minValue (defaults to
22562      * 'The date in this field must be after {minValue}').
22563      */
22564     minText : "The date in this field must be equal to or after {0}",
22565     /**
22566      * @cfg {String} maxText
22567      * The error text to display when the date in the cell is after maxValue (defaults to
22568      * 'The date in this field must be before {maxValue}').
22569      */
22570     maxText : "The date in this field must be equal to or before {0}",
22571     /**
22572      * @cfg {String} invalidText
22573      * The error text to display when the date in the field is invalid (defaults to
22574      * '{value} is not a valid date - it must be in the format {format}').
22575      */
22576     invalidText : "{0} is not a valid date - it must be in the format {1}",
22577     /**
22578      * @cfg {String} triggerClass
22579      * An additional CSS class used to style the trigger button.  The trigger will always get the
22580      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22581      * which displays a calendar icon).
22582      */
22583     triggerClass : 'x-form-date-trigger',
22584     
22585
22586     /**
22587      * @cfg {bool} useIso
22588      * if enabled, then the date field will use a hidden field to store the 
22589      * real value as iso formated date. default (false)
22590      */ 
22591     useIso : false,
22592     /**
22593      * @cfg {String/Object} autoCreate
22594      * A DomHelper element spec, or true for a default element spec (defaults to
22595      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22596      */ 
22597     // private
22598     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22599     
22600     // private
22601     hiddenField: false,
22602     
22603     onRender : function(ct, position)
22604     {
22605         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22606         if (this.useIso) {
22607             this.el.dom.removeAttribute('name'); 
22608             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22609                     'before', true);
22610             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
22611             // prevent input submission
22612             this.hiddenName = this.name;
22613         }
22614             
22615             
22616     },
22617     
22618     // private
22619     validateValue : function(value)
22620     {
22621         value = this.formatDate(value);
22622         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22623             return false;
22624         }
22625         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22626              return true;
22627         }
22628         var svalue = value;
22629         value = this.parseDate(value);
22630         if(!value){
22631             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22632             return false;
22633         }
22634         var time = value.getTime();
22635         if(this.minValue && time < this.minValue.getTime()){
22636             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22637             return false;
22638         }
22639         if(this.maxValue && time > this.maxValue.getTime()){
22640             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22641             return false;
22642         }
22643         if(this.disabledDays){
22644             var day = value.getDay();
22645             for(var i = 0; i < this.disabledDays.length; i++) {
22646                 if(day === this.disabledDays[i]){
22647                     this.markInvalid(this.disabledDaysText);
22648                     return false;
22649                 }
22650             }
22651         }
22652         var fvalue = this.formatDate(value);
22653         if(this.ddMatch && this.ddMatch.test(fvalue)){
22654             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22655             return false;
22656         }
22657         return true;
22658     },
22659
22660     // private
22661     // Provides logic to override the default TriggerField.validateBlur which just returns true
22662     validateBlur : function(){
22663         return !this.menu || !this.menu.isVisible();
22664     },
22665
22666     /**
22667      * Returns the current date value of the date field.
22668      * @return {Date} The date value
22669      */
22670     getValue : function(){
22671         
22672         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22673     },
22674
22675     /**
22676      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22677      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22678      * (the default format used is "m/d/y").
22679      * <br />Usage:
22680      * <pre><code>
22681 //All of these calls set the same date value (May 4, 2006)
22682
22683 //Pass a date object:
22684 var dt = new Date('5/4/06');
22685 dateField.setValue(dt);
22686
22687 //Pass a date string (default format):
22688 dateField.setValue('5/4/06');
22689
22690 //Pass a date string (custom format):
22691 dateField.format = 'Y-m-d';
22692 dateField.setValue('2006-5-4');
22693 </code></pre>
22694      * @param {String/Date} date The date or valid date string
22695      */
22696     setValue : function(date){
22697         if (this.hiddenField) {
22698             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22699         }
22700         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22701     },
22702
22703     // private
22704     parseDate : function(value){
22705         if(!value || value instanceof Date){
22706             return value;
22707         }
22708         var v = Date.parseDate(value, this.format);
22709         if(!v && this.altFormats){
22710             if(!this.altFormatsArray){
22711                 this.altFormatsArray = this.altFormats.split("|");
22712             }
22713             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22714                 v = Date.parseDate(value, this.altFormatsArray[i]);
22715             }
22716         }
22717         return v;
22718     },
22719
22720     // private
22721     formatDate : function(date, fmt){
22722         return (!date || !(date instanceof Date)) ?
22723                date : date.dateFormat(fmt || this.format);
22724     },
22725
22726     // private
22727     menuListeners : {
22728         select: function(m, d){
22729             this.setValue(d);
22730             this.fireEvent('select', this, d);
22731         },
22732         show : function(){ // retain focus styling
22733             this.onFocus();
22734         },
22735         hide : function(){
22736             this.focus.defer(10, this);
22737             var ml = this.menuListeners;
22738             this.menu.un("select", ml.select,  this);
22739             this.menu.un("show", ml.show,  this);
22740             this.menu.un("hide", ml.hide,  this);
22741         }
22742     },
22743
22744     // private
22745     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22746     onTriggerClick : function(){
22747         if(this.disabled){
22748             return;
22749         }
22750         if(this.menu == null){
22751             this.menu = new Roo.menu.DateMenu();
22752         }
22753         Roo.apply(this.menu.picker,  {
22754             showClear: this.allowBlank,
22755             minDate : this.minValue,
22756             maxDate : this.maxValue,
22757             disabledDatesRE : this.ddMatch,
22758             disabledDatesText : this.disabledDatesText,
22759             disabledDays : this.disabledDays,
22760             disabledDaysText : this.disabledDaysText,
22761             format : this.format,
22762             minText : String.format(this.minText, this.formatDate(this.minValue)),
22763             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22764         });
22765         this.menu.on(Roo.apply({}, this.menuListeners, {
22766             scope:this
22767         }));
22768         this.menu.picker.setValue(this.getValue() || new Date());
22769         this.menu.show(this.el, "tl-bl?");
22770     },
22771
22772     beforeBlur : function(){
22773         var v = this.parseDate(this.getRawValue());
22774         if(v){
22775             this.setValue(v);
22776         }
22777     }
22778
22779     /** @cfg {Boolean} grow @hide */
22780     /** @cfg {Number} growMin @hide */
22781     /** @cfg {Number} growMax @hide */
22782     /**
22783      * @hide
22784      * @method autoSize
22785      */
22786 });/*
22787  * Based on:
22788  * Ext JS Library 1.1.1
22789  * Copyright(c) 2006-2007, Ext JS, LLC.
22790  *
22791  * Originally Released Under LGPL - original licence link has changed is not relivant.
22792  *
22793  * Fork - LGPL
22794  * <script type="text/javascript">
22795  */
22796  
22797
22798 /**
22799  * @class Roo.form.ComboBox
22800  * @extends Roo.form.TriggerField
22801  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22802  * @constructor
22803  * Create a new ComboBox.
22804  * @param {Object} config Configuration options
22805  */
22806 Roo.form.ComboBox = function(config){
22807     Roo.form.ComboBox.superclass.constructor.call(this, config);
22808     this.addEvents({
22809         /**
22810          * @event expand
22811          * Fires when the dropdown list is expanded
22812              * @param {Roo.form.ComboBox} combo This combo box
22813              */
22814         'expand' : true,
22815         /**
22816          * @event collapse
22817          * Fires when the dropdown list is collapsed
22818              * @param {Roo.form.ComboBox} combo This combo box
22819              */
22820         'collapse' : true,
22821         /**
22822          * @event beforeselect
22823          * Fires before a list item is selected. Return false to cancel the selection.
22824              * @param {Roo.form.ComboBox} combo This combo box
22825              * @param {Roo.data.Record} record The data record returned from the underlying store
22826              * @param {Number} index The index of the selected item in the dropdown list
22827              */
22828         'beforeselect' : true,
22829         /**
22830          * @event select
22831          * Fires when a list item is selected
22832              * @param {Roo.form.ComboBox} combo This combo box
22833              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22834              * @param {Number} index The index of the selected item in the dropdown list
22835              */
22836         'select' : true,
22837         /**
22838          * @event beforequery
22839          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22840          * The event object passed has these properties:
22841              * @param {Roo.form.ComboBox} combo This combo box
22842              * @param {String} query The query
22843              * @param {Boolean} forceAll true to force "all" query
22844              * @param {Boolean} cancel true to cancel the query
22845              * @param {Object} e The query event object
22846              */
22847         'beforequery': true,
22848          /**
22849          * @event add
22850          * Fires when the 'add' icon is pressed (add a listener to enable add button)
22851              * @param {Roo.form.ComboBox} combo This combo box
22852              */
22853         'add' : true,
22854         /**
22855          * @event edit
22856          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
22857              * @param {Roo.form.ComboBox} combo This combo box
22858              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
22859              */
22860         'edit' : true
22861         
22862         
22863     });
22864     if(this.transform){
22865         this.allowDomMove = false;
22866         var s = Roo.getDom(this.transform);
22867         if(!this.hiddenName){
22868             this.hiddenName = s.name;
22869         }
22870         if(!this.store){
22871             this.mode = 'local';
22872             var d = [], opts = s.options;
22873             for(var i = 0, len = opts.length;i < len; i++){
22874                 var o = opts[i];
22875                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22876                 if(o.selected) {
22877                     this.value = value;
22878                 }
22879                 d.push([value, o.text]);
22880             }
22881             this.store = new Roo.data.SimpleStore({
22882                 'id': 0,
22883                 fields: ['value', 'text'],
22884                 data : d
22885             });
22886             this.valueField = 'value';
22887             this.displayField = 'text';
22888         }
22889         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22890         if(!this.lazyRender){
22891             this.target = true;
22892             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22893             s.parentNode.removeChild(s); // remove it
22894             this.render(this.el.parentNode);
22895         }else{
22896             s.parentNode.removeChild(s); // remove it
22897         }
22898
22899     }
22900     if (this.store) {
22901         this.store = Roo.factory(this.store, Roo.data);
22902     }
22903     
22904     this.selectedIndex = -1;
22905     if(this.mode == 'local'){
22906         if(config.queryDelay === undefined){
22907             this.queryDelay = 10;
22908         }
22909         if(config.minChars === undefined){
22910             this.minChars = 0;
22911         }
22912     }
22913 };
22914
22915 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22916     /**
22917      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22918      */
22919     /**
22920      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
22921      * rendering into an Roo.Editor, defaults to false)
22922      */
22923     /**
22924      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
22925      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
22926      */
22927     /**
22928      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
22929      */
22930     /**
22931      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
22932      * the dropdown list (defaults to undefined, with no header element)
22933      */
22934
22935      /**
22936      * @cfg {String/Roo.Template} tpl The template to use to render the output
22937      */
22938      
22939     // private
22940     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
22941     /**
22942      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
22943      */
22944     listWidth: undefined,
22945     /**
22946      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
22947      * mode = 'remote' or 'text' if mode = 'local')
22948      */
22949     displayField: undefined,
22950     /**
22951      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
22952      * mode = 'remote' or 'value' if mode = 'local'). 
22953      * Note: use of a valueField requires the user make a selection
22954      * in order for a value to be mapped.
22955      */
22956     valueField: undefined,
22957     
22958     
22959     /**
22960      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
22961      * field's data value (defaults to the underlying DOM element's name)
22962      */
22963     hiddenName: undefined,
22964     /**
22965      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
22966      */
22967     listClass: '',
22968     /**
22969      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
22970      */
22971     selectedClass: 'x-combo-selected',
22972     /**
22973      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22974      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
22975      * which displays a downward arrow icon).
22976      */
22977     triggerClass : 'x-form-arrow-trigger',
22978     /**
22979      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
22980      */
22981     shadow:'sides',
22982     /**
22983      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
22984      * anchor positions (defaults to 'tl-bl')
22985      */
22986     listAlign: 'tl-bl?',
22987     /**
22988      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
22989      */
22990     maxHeight: 300,
22991     /**
22992      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
22993      * query specified by the allQuery config option (defaults to 'query')
22994      */
22995     triggerAction: 'query',
22996     /**
22997      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
22998      * (defaults to 4, does not apply if editable = false)
22999      */
23000     minChars : 4,
23001     /**
23002      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
23003      * delay (typeAheadDelay) if it matches a known value (defaults to false)
23004      */
23005     typeAhead: false,
23006     /**
23007      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
23008      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
23009      */
23010     queryDelay: 500,
23011     /**
23012      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
23013      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
23014      */
23015     pageSize: 0,
23016     /**
23017      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
23018      * when editable = true (defaults to false)
23019      */
23020     selectOnFocus:false,
23021     /**
23022      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
23023      */
23024     queryParam: 'query',
23025     /**
23026      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
23027      * when mode = 'remote' (defaults to 'Loading...')
23028      */
23029     loadingText: 'Loading...',
23030     /**
23031      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
23032      */
23033     resizable: false,
23034     /**
23035      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
23036      */
23037     handleHeight : 8,
23038     /**
23039      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
23040      * traditional select (defaults to true)
23041      */
23042     editable: true,
23043     /**
23044      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
23045      */
23046     allQuery: '',
23047     /**
23048      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
23049      */
23050     mode: 'remote',
23051     /**
23052      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
23053      * listWidth has a higher value)
23054      */
23055     minListWidth : 70,
23056     /**
23057      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
23058      * allow the user to set arbitrary text into the field (defaults to false)
23059      */
23060     forceSelection:false,
23061     /**
23062      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
23063      * if typeAhead = true (defaults to 250)
23064      */
23065     typeAheadDelay : 250,
23066     /**
23067      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
23068      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
23069      */
23070     valueNotFoundText : undefined,
23071     /**
23072      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
23073      */
23074     blockFocus : false,
23075     
23076     /**
23077      * @cfg {Boolean} disableClear Disable showing of clear button.
23078      */
23079     disableClear : false,
23080     /**
23081      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
23082      */
23083     alwaysQuery : false,
23084     
23085     //private
23086     addicon : false,
23087     editicon: false,
23088     
23089     // element that contains real text value.. (when hidden is used..)
23090      
23091     // private
23092     onRender : function(ct, position){
23093         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
23094         if(this.hiddenName){
23095             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
23096                     'before', true);
23097             this.hiddenField.value =
23098                 this.hiddenValue !== undefined ? this.hiddenValue :
23099                 this.value !== undefined ? this.value : '';
23100
23101             // prevent input submission
23102             this.el.dom.removeAttribute('name');
23103              
23104              
23105         }
23106         if(Roo.isGecko){
23107             this.el.dom.setAttribute('autocomplete', 'off');
23108         }
23109
23110         var cls = 'x-combo-list';
23111
23112         this.list = new Roo.Layer({
23113             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
23114         });
23115
23116         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
23117         this.list.setWidth(lw);
23118         this.list.swallowEvent('mousewheel');
23119         this.assetHeight = 0;
23120
23121         if(this.title){
23122             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
23123             this.assetHeight += this.header.getHeight();
23124         }
23125
23126         this.innerList = this.list.createChild({cls:cls+'-inner'});
23127         this.innerList.on('mouseover', this.onViewOver, this);
23128         this.innerList.on('mousemove', this.onViewMove, this);
23129         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23130         
23131         if(this.allowBlank && !this.pageSize && !this.disableClear){
23132             this.footer = this.list.createChild({cls:cls+'-ft'});
23133             this.pageTb = new Roo.Toolbar(this.footer);
23134            
23135         }
23136         if(this.pageSize){
23137             this.footer = this.list.createChild({cls:cls+'-ft'});
23138             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
23139                     {pageSize: this.pageSize});
23140             
23141         }
23142         
23143         if (this.pageTb && this.allowBlank && !this.disableClear) {
23144             var _this = this;
23145             this.pageTb.add(new Roo.Toolbar.Fill(), {
23146                 cls: 'x-btn-icon x-btn-clear',
23147                 text: '&#160;',
23148                 handler: function()
23149                 {
23150                     _this.collapse();
23151                     _this.clearValue();
23152                     _this.onSelect(false, -1);
23153                 }
23154             });
23155         }
23156         if (this.footer) {
23157             this.assetHeight += this.footer.getHeight();
23158         }
23159         
23160
23161         if(!this.tpl){
23162             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
23163         }
23164
23165         this.view = new Roo.View(this.innerList, this.tpl, {
23166             singleSelect:true, store: this.store, selectedClass: this.selectedClass
23167         });
23168
23169         this.view.on('click', this.onViewClick, this);
23170
23171         this.store.on('beforeload', this.onBeforeLoad, this);
23172         this.store.on('load', this.onLoad, this);
23173         this.store.on('loadexception', this.onLoadException, this);
23174
23175         if(this.resizable){
23176             this.resizer = new Roo.Resizable(this.list,  {
23177                pinned:true, handles:'se'
23178             });
23179             this.resizer.on('resize', function(r, w, h){
23180                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23181                 this.listWidth = w;
23182                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23183                 this.restrictHeight();
23184             }, this);
23185             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23186         }
23187         if(!this.editable){
23188             this.editable = true;
23189             this.setEditable(false);
23190         }  
23191         
23192         
23193         if (typeof(this.events.add.listeners) != 'undefined') {
23194             
23195             this.addicon = this.wrap.createChild(
23196                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
23197        
23198             this.addicon.on('click', function(e) {
23199                 this.fireEvent('add', this);
23200             }, this);
23201         }
23202         if (typeof(this.events.edit.listeners) != 'undefined') {
23203             
23204             this.editicon = this.wrap.createChild(
23205                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
23206             if (this.addicon) {
23207                 this.editicon.setStyle('margin-left', '40px');
23208             }
23209             this.editicon.on('click', function(e) {
23210                 
23211                 // we fire even  if inothing is selected..
23212                 this.fireEvent('edit', this, this.lastData );
23213                 
23214             }, this);
23215         }
23216         
23217         
23218         
23219     },
23220
23221     // private
23222     initEvents : function(){
23223         Roo.form.ComboBox.superclass.initEvents.call(this);
23224
23225         this.keyNav = new Roo.KeyNav(this.el, {
23226             "up" : function(e){
23227                 this.inKeyMode = true;
23228                 this.selectPrev();
23229             },
23230
23231             "down" : function(e){
23232                 if(!this.isExpanded()){
23233                     this.onTriggerClick();
23234                 }else{
23235                     this.inKeyMode = true;
23236                     this.selectNext();
23237                 }
23238             },
23239
23240             "enter" : function(e){
23241                 this.onViewClick();
23242                 //return true;
23243             },
23244
23245             "esc" : function(e){
23246                 this.collapse();
23247             },
23248
23249             "tab" : function(e){
23250                 this.onViewClick(false);
23251                 this.fireEvent("specialkey", this, e);
23252                 return true;
23253             },
23254
23255             scope : this,
23256
23257             doRelay : function(foo, bar, hname){
23258                 if(hname == 'down' || this.scope.isExpanded()){
23259                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23260                 }
23261                 return true;
23262             },
23263
23264             forceKeyDown: true
23265         });
23266         this.queryDelay = Math.max(this.queryDelay || 10,
23267                 this.mode == 'local' ? 10 : 250);
23268         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23269         if(this.typeAhead){
23270             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23271         }
23272         if(this.editable !== false){
23273             this.el.on("keyup", this.onKeyUp, this);
23274         }
23275         if(this.forceSelection){
23276             this.on('blur', this.doForce, this);
23277         }
23278     },
23279
23280     onDestroy : function(){
23281         if(this.view){
23282             this.view.setStore(null);
23283             this.view.el.removeAllListeners();
23284             this.view.el.remove();
23285             this.view.purgeListeners();
23286         }
23287         if(this.list){
23288             this.list.destroy();
23289         }
23290         if(this.store){
23291             this.store.un('beforeload', this.onBeforeLoad, this);
23292             this.store.un('load', this.onLoad, this);
23293             this.store.un('loadexception', this.onLoadException, this);
23294         }
23295         Roo.form.ComboBox.superclass.onDestroy.call(this);
23296     },
23297
23298     // private
23299     fireKey : function(e){
23300         if(e.isNavKeyPress() && !this.list.isVisible()){
23301             this.fireEvent("specialkey", this, e);
23302         }
23303     },
23304
23305     // private
23306     onResize: function(w, h){
23307         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23308         
23309         if(typeof w != 'number'){
23310             // we do not handle it!?!?
23311             return;
23312         }
23313         var tw = this.trigger.getWidth();
23314         tw += this.addicon ? this.addicon.getWidth() : 0;
23315         tw += this.editicon ? this.editicon.getWidth() : 0;
23316         var x = w - tw;
23317         this.el.setWidth( this.adjustWidth('input', x));
23318             
23319         this.trigger.setStyle('left', x+'px');
23320         
23321         if(this.list && this.listWidth === undefined){
23322             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23323             this.list.setWidth(lw);
23324             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23325         }
23326         
23327     
23328         
23329     },
23330
23331     /**
23332      * Allow or prevent the user from directly editing the field text.  If false is passed,
23333      * the user will only be able to select from the items defined in the dropdown list.  This method
23334      * is the runtime equivalent of setting the 'editable' config option at config time.
23335      * @param {Boolean} value True to allow the user to directly edit the field text
23336      */
23337     setEditable : function(value){
23338         if(value == this.editable){
23339             return;
23340         }
23341         this.editable = value;
23342         if(!value){
23343             this.el.dom.setAttribute('readOnly', true);
23344             this.el.on('mousedown', this.onTriggerClick,  this);
23345             this.el.addClass('x-combo-noedit');
23346         }else{
23347             this.el.dom.setAttribute('readOnly', false);
23348             this.el.un('mousedown', this.onTriggerClick,  this);
23349             this.el.removeClass('x-combo-noedit');
23350         }
23351     },
23352
23353     // private
23354     onBeforeLoad : function(){
23355         if(!this.hasFocus){
23356             return;
23357         }
23358         this.innerList.update(this.loadingText ?
23359                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23360         this.restrictHeight();
23361         this.selectedIndex = -1;
23362     },
23363
23364     // private
23365     onLoad : function(){
23366         if(!this.hasFocus){
23367             return;
23368         }
23369         if(this.store.getCount() > 0){
23370             this.expand();
23371             this.restrictHeight();
23372             if(this.lastQuery == this.allQuery){
23373                 if(this.editable){
23374                     this.el.dom.select();
23375                 }
23376                 if(!this.selectByValue(this.value, true)){
23377                     this.select(0, true);
23378                 }
23379             }else{
23380                 this.selectNext();
23381                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23382                     this.taTask.delay(this.typeAheadDelay);
23383                 }
23384             }
23385         }else{
23386             this.onEmptyResults();
23387         }
23388         //this.el.focus();
23389     },
23390     // private
23391     onLoadException : function()
23392     {
23393         this.collapse();
23394         Roo.log(this.store.reader.jsonData);
23395         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
23396             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
23397         }
23398         
23399         
23400     },
23401     // private
23402     onTypeAhead : function(){
23403         if(this.store.getCount() > 0){
23404             var r = this.store.getAt(0);
23405             var newValue = r.data[this.displayField];
23406             var len = newValue.length;
23407             var selStart = this.getRawValue().length;
23408             if(selStart != len){
23409                 this.setRawValue(newValue);
23410                 this.selectText(selStart, newValue.length);
23411             }
23412         }
23413     },
23414
23415     // private
23416     onSelect : function(record, index){
23417         if(this.fireEvent('beforeselect', this, record, index) !== false){
23418             this.setFromData(index > -1 ? record.data : false);
23419             this.collapse();
23420             this.fireEvent('select', this, record, index);
23421         }
23422     },
23423
23424     /**
23425      * Returns the currently selected field value or empty string if no value is set.
23426      * @return {String} value The selected value
23427      */
23428     getValue : function(){
23429         if(this.valueField){
23430             return typeof this.value != 'undefined' ? this.value : '';
23431         }else{
23432             return Roo.form.ComboBox.superclass.getValue.call(this);
23433         }
23434     },
23435
23436     /**
23437      * Clears any text/value currently set in the field
23438      */
23439     clearValue : function(){
23440         if(this.hiddenField){
23441             this.hiddenField.value = '';
23442         }
23443         this.value = '';
23444         this.setRawValue('');
23445         this.lastSelectionText = '';
23446         this.applyEmptyText();
23447     },
23448
23449     /**
23450      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23451      * will be displayed in the field.  If the value does not match the data value of an existing item,
23452      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23453      * Otherwise the field will be blank (although the value will still be set).
23454      * @param {String} value The value to match
23455      */
23456     setValue : function(v){
23457         var text = v;
23458         if(this.valueField){
23459             var r = this.findRecord(this.valueField, v);
23460             if(r){
23461                 text = r.data[this.displayField];
23462             }else if(this.valueNotFoundText !== undefined){
23463                 text = this.valueNotFoundText;
23464             }
23465         }
23466         this.lastSelectionText = text;
23467         if(this.hiddenField){
23468             this.hiddenField.value = v;
23469         }
23470         Roo.form.ComboBox.superclass.setValue.call(this, text);
23471         this.value = v;
23472     },
23473     /**
23474      * @property {Object} the last set data for the element
23475      */
23476     
23477     lastData : false,
23478     /**
23479      * Sets the value of the field based on a object which is related to the record format for the store.
23480      * @param {Object} value the value to set as. or false on reset?
23481      */
23482     setFromData : function(o){
23483         var dv = ''; // display value
23484         var vv = ''; // value value..
23485         this.lastData = o;
23486         if (this.displayField) {
23487             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23488         } else {
23489             // this is an error condition!!!
23490             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23491         }
23492         
23493         if(this.valueField){
23494             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23495         }
23496         if(this.hiddenField){
23497             this.hiddenField.value = vv;
23498             
23499             this.lastSelectionText = dv;
23500             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23501             this.value = vv;
23502             return;
23503         }
23504         // no hidden field.. - we store the value in 'value', but still display
23505         // display field!!!!
23506         this.lastSelectionText = dv;
23507         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23508         this.value = vv;
23509         
23510         
23511     },
23512     // private
23513     reset : function(){
23514         // overridden so that last data is reset..
23515         this.setValue(this.originalValue);
23516         this.clearInvalid();
23517         this.lastData = false;
23518     },
23519     // private
23520     findRecord : function(prop, value){
23521         var record;
23522         if(this.store.getCount() > 0){
23523             this.store.each(function(r){
23524                 if(r.data[prop] == value){
23525                     record = r;
23526                     return false;
23527                 }
23528                 return true;
23529             });
23530         }
23531         return record;
23532     },
23533     
23534     getName: function()
23535     {
23536         // returns hidden if it's set..
23537         if (!this.rendered) {return ''};
23538         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
23539         
23540     },
23541     // private
23542     onViewMove : function(e, t){
23543         this.inKeyMode = false;
23544     },
23545
23546     // private
23547     onViewOver : function(e, t){
23548         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23549             return;
23550         }
23551         var item = this.view.findItemFromChild(t);
23552         if(item){
23553             var index = this.view.indexOf(item);
23554             this.select(index, false);
23555         }
23556     },
23557
23558     // private
23559     onViewClick : function(doFocus)
23560     {
23561         var index = this.view.getSelectedIndexes()[0];
23562         var r = this.store.getAt(index);
23563         if(r){
23564             this.onSelect(r, index);
23565         }
23566         if(doFocus !== false && !this.blockFocus){
23567             this.el.focus();
23568         }
23569     },
23570
23571     // private
23572     restrictHeight : function(){
23573         this.innerList.dom.style.height = '';
23574         var inner = this.innerList.dom;
23575         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23576         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23577         this.list.beginUpdate();
23578         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23579         this.list.alignTo(this.el, this.listAlign);
23580         this.list.endUpdate();
23581     },
23582
23583     // private
23584     onEmptyResults : function(){
23585         this.collapse();
23586     },
23587
23588     /**
23589      * Returns true if the dropdown list is expanded, else false.
23590      */
23591     isExpanded : function(){
23592         return this.list.isVisible();
23593     },
23594
23595     /**
23596      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23597      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23598      * @param {String} value The data value of the item to select
23599      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23600      * selected item if it is not currently in view (defaults to true)
23601      * @return {Boolean} True if the value matched an item in the list, else false
23602      */
23603     selectByValue : function(v, scrollIntoView){
23604         if(v !== undefined && v !== null){
23605             var r = this.findRecord(this.valueField || this.displayField, v);
23606             if(r){
23607                 this.select(this.store.indexOf(r), scrollIntoView);
23608                 return true;
23609             }
23610         }
23611         return false;
23612     },
23613
23614     /**
23615      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23616      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23617      * @param {Number} index The zero-based index of the list item to select
23618      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23619      * selected item if it is not currently in view (defaults to true)
23620      */
23621     select : function(index, scrollIntoView){
23622         this.selectedIndex = index;
23623         this.view.select(index);
23624         if(scrollIntoView !== false){
23625             var el = this.view.getNode(index);
23626             if(el){
23627                 this.innerList.scrollChildIntoView(el, false);
23628             }
23629         }
23630     },
23631
23632     // private
23633     selectNext : function(){
23634         var ct = this.store.getCount();
23635         if(ct > 0){
23636             if(this.selectedIndex == -1){
23637                 this.select(0);
23638             }else if(this.selectedIndex < ct-1){
23639                 this.select(this.selectedIndex+1);
23640             }
23641         }
23642     },
23643
23644     // private
23645     selectPrev : function(){
23646         var ct = this.store.getCount();
23647         if(ct > 0){
23648             if(this.selectedIndex == -1){
23649                 this.select(0);
23650             }else if(this.selectedIndex != 0){
23651                 this.select(this.selectedIndex-1);
23652             }
23653         }
23654     },
23655
23656     // private
23657     onKeyUp : function(e){
23658         if(this.editable !== false && !e.isSpecialKey()){
23659             this.lastKey = e.getKey();
23660             this.dqTask.delay(this.queryDelay);
23661         }
23662     },
23663
23664     // private
23665     validateBlur : function(){
23666         return !this.list || !this.list.isVisible();   
23667     },
23668
23669     // private
23670     initQuery : function(){
23671         this.doQuery(this.getRawValue());
23672     },
23673
23674     // private
23675     doForce : function(){
23676         if(this.el.dom.value.length > 0){
23677             this.el.dom.value =
23678                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23679             this.applyEmptyText();
23680         }
23681     },
23682
23683     /**
23684      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23685      * query allowing the query action to be canceled if needed.
23686      * @param {String} query The SQL query to execute
23687      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23688      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23689      * saved in the current store (defaults to false)
23690      */
23691     doQuery : function(q, forceAll){
23692         if(q === undefined || q === null){
23693             q = '';
23694         }
23695         var qe = {
23696             query: q,
23697             forceAll: forceAll,
23698             combo: this,
23699             cancel:false
23700         };
23701         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23702             return false;
23703         }
23704         q = qe.query;
23705         forceAll = qe.forceAll;
23706         if(forceAll === true || (q.length >= this.minChars)){
23707             if(this.lastQuery != q || this.alwaysQuery){
23708                 this.lastQuery = q;
23709                 if(this.mode == 'local'){
23710                     this.selectedIndex = -1;
23711                     if(forceAll){
23712                         this.store.clearFilter();
23713                     }else{
23714                         this.store.filter(this.displayField, q);
23715                     }
23716                     this.onLoad();
23717                 }else{
23718                     this.store.baseParams[this.queryParam] = q;
23719                     this.store.load({
23720                         params: this.getParams(q)
23721                     });
23722                     this.expand();
23723                 }
23724             }else{
23725                 this.selectedIndex = -1;
23726                 this.onLoad();   
23727             }
23728         }
23729     },
23730
23731     // private
23732     getParams : function(q){
23733         var p = {};
23734         //p[this.queryParam] = q;
23735         if(this.pageSize){
23736             p.start = 0;
23737             p.limit = this.pageSize;
23738         }
23739         return p;
23740     },
23741
23742     /**
23743      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23744      */
23745     collapse : function(){
23746         if(!this.isExpanded()){
23747             return;
23748         }
23749         this.list.hide();
23750         Roo.get(document).un('mousedown', this.collapseIf, this);
23751         Roo.get(document).un('mousewheel', this.collapseIf, this);
23752         if (!this.editable) {
23753             Roo.get(document).un('keydown', this.listKeyPress, this);
23754         }
23755         this.fireEvent('collapse', this);
23756     },
23757
23758     // private
23759     collapseIf : function(e){
23760         if(!e.within(this.wrap) && !e.within(this.list)){
23761             this.collapse();
23762         }
23763     },
23764
23765     /**
23766      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23767      */
23768     expand : function(){
23769         if(this.isExpanded() || !this.hasFocus){
23770             return;
23771         }
23772         this.list.alignTo(this.el, this.listAlign);
23773         this.list.show();
23774         Roo.get(document).on('mousedown', this.collapseIf, this);
23775         Roo.get(document).on('mousewheel', this.collapseIf, this);
23776         if (!this.editable) {
23777             Roo.get(document).on('keydown', this.listKeyPress, this);
23778         }
23779         
23780         this.fireEvent('expand', this);
23781     },
23782
23783     // private
23784     // Implements the default empty TriggerField.onTriggerClick function
23785     onTriggerClick : function(){
23786         if(this.disabled){
23787             return;
23788         }
23789         if(this.isExpanded()){
23790             this.collapse();
23791             if (!this.blockFocus) {
23792                 this.el.focus();
23793             }
23794             
23795         }else {
23796             this.hasFocus = true;
23797             if(this.triggerAction == 'all') {
23798                 this.doQuery(this.allQuery, true);
23799             } else {
23800                 this.doQuery(this.getRawValue());
23801             }
23802             if (!this.blockFocus) {
23803                 this.el.focus();
23804             }
23805         }
23806     },
23807     listKeyPress : function(e)
23808     {
23809         //Roo.log('listkeypress');
23810         // scroll to first matching element based on key pres..
23811         if (e.isSpecialKey()) {
23812             return false;
23813         }
23814         var k = String.fromCharCode(e.getKey()).toUpperCase();
23815         //Roo.log(k);
23816         var match  = false;
23817         var csel = this.view.getSelectedNodes();
23818         var cselitem = false;
23819         if (csel.length) {
23820             var ix = this.view.indexOf(csel[0]);
23821             cselitem  = this.store.getAt(ix);
23822             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
23823                 cselitem = false;
23824             }
23825             
23826         }
23827         
23828         this.store.each(function(v) { 
23829             if (cselitem) {
23830                 // start at existing selection.
23831                 if (cselitem.id == v.id) {
23832                     cselitem = false;
23833                 }
23834                 return;
23835             }
23836                 
23837             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
23838                 match = this.store.indexOf(v);
23839                 return false;
23840             }
23841         }, this);
23842         
23843         if (match === false) {
23844             return true; // no more action?
23845         }
23846         // scroll to?
23847         this.view.select(match);
23848         var sn = Roo.get(this.view.getSelectedNodes()[0])
23849         sn.scrollIntoView(sn.dom.parentNode, false);
23850     }
23851
23852     /** 
23853     * @cfg {Boolean} grow 
23854     * @hide 
23855     */
23856     /** 
23857     * @cfg {Number} growMin 
23858     * @hide 
23859     */
23860     /** 
23861     * @cfg {Number} growMax 
23862     * @hide 
23863     */
23864     /**
23865      * @hide
23866      * @method autoSize
23867      */
23868 });/*
23869  * Based on:
23870  * Ext JS Library 1.1.1
23871  * Copyright(c) 2006-2007, Ext JS, LLC.
23872  *
23873  * Originally Released Under LGPL - original licence link has changed is not relivant.
23874  *
23875  * Fork - LGPL
23876  * <script type="text/javascript">
23877  */
23878 /**
23879  * @class Roo.form.Checkbox
23880  * @extends Roo.form.Field
23881  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
23882  * @constructor
23883  * Creates a new Checkbox
23884  * @param {Object} config Configuration options
23885  */
23886 Roo.form.Checkbox = function(config){
23887     Roo.form.Checkbox.superclass.constructor.call(this, config);
23888     this.addEvents({
23889         /**
23890          * @event check
23891          * Fires when the checkbox is checked or unchecked.
23892              * @param {Roo.form.Checkbox} this This checkbox
23893              * @param {Boolean} checked The new checked value
23894              */
23895         check : true
23896     });
23897 };
23898
23899 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
23900     /**
23901      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
23902      */
23903     focusClass : undefined,
23904     /**
23905      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
23906      */
23907     fieldClass: "x-form-field",
23908     /**
23909      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
23910      */
23911     checked: false,
23912     /**
23913      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
23914      * {tag: "input", type: "checkbox", autocomplete: "off"})
23915      */
23916     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
23917     /**
23918      * @cfg {String} boxLabel The text that appears beside the checkbox
23919      */
23920     boxLabel : "",
23921     /**
23922      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
23923      */  
23924     inputValue : '1',
23925     /**
23926      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23927      */
23928      valueOff: '0', // value when not checked..
23929
23930     actionMode : 'viewEl', 
23931     //
23932     // private
23933     itemCls : 'x-menu-check-item x-form-item',
23934     groupClass : 'x-menu-group-item',
23935     inputType : 'hidden',
23936     
23937     
23938     inSetChecked: false, // check that we are not calling self...
23939     
23940     inputElement: false, // real input element?
23941     basedOn: false, // ????
23942     
23943     isFormField: true, // not sure where this is needed!!!!
23944
23945     onResize : function(){
23946         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
23947         if(!this.boxLabel){
23948             this.el.alignTo(this.wrap, 'c-c');
23949         }
23950     },
23951
23952     initEvents : function(){
23953         Roo.form.Checkbox.superclass.initEvents.call(this);
23954         this.el.on("click", this.onClick,  this);
23955         this.el.on("change", this.onClick,  this);
23956     },
23957
23958
23959     getResizeEl : function(){
23960         return this.wrap;
23961     },
23962
23963     getPositionEl : function(){
23964         return this.wrap;
23965     },
23966
23967     // private
23968     onRender : function(ct, position){
23969         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
23970         /*
23971         if(this.inputValue !== undefined){
23972             this.el.dom.value = this.inputValue;
23973         }
23974         */
23975         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
23976         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
23977         var viewEl = this.wrap.createChild({ 
23978             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
23979         this.viewEl = viewEl;   
23980         this.wrap.on('click', this.onClick,  this); 
23981         
23982         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
23983         this.el.on('propertychange', this.setFromHidden,  this);  //ie
23984         
23985         
23986         
23987         if(this.boxLabel){
23988             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
23989         //    viewEl.on('click', this.onClick,  this); 
23990         }
23991         //if(this.checked){
23992             this.setChecked(this.checked);
23993         //}else{
23994             //this.checked = this.el.dom;
23995         //}
23996
23997     },
23998
23999     // private
24000     initValue : Roo.emptyFn,
24001
24002     /**
24003      * Returns the checked state of the checkbox.
24004      * @return {Boolean} True if checked, else false
24005      */
24006     getValue : function(){
24007         if(this.el){
24008             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
24009         }
24010         return this.valueOff;
24011         
24012     },
24013
24014         // private
24015     onClick : function(){ 
24016         this.setChecked(!this.checked);
24017
24018         //if(this.el.dom.checked != this.checked){
24019         //    this.setValue(this.el.dom.checked);
24020        // }
24021     },
24022
24023     /**
24024      * Sets the checked state of the checkbox.
24025      * On is always based on a string comparison between inputValue and the param.
24026      * @param {Boolean/String} value - the value to set 
24027      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
24028      */
24029     setValue : function(v,suppressEvent){
24030         
24031         
24032         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
24033         //if(this.el && this.el.dom){
24034         //    this.el.dom.checked = this.checked;
24035         //    this.el.dom.defaultChecked = this.checked;
24036         //}
24037         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
24038         //this.fireEvent("check", this, this.checked);
24039     },
24040     // private..
24041     setChecked : function(state,suppressEvent)
24042     {
24043         if (this.inSetChecked) {
24044             this.checked = state;
24045             return;
24046         }
24047         
24048     
24049         if(this.wrap){
24050             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
24051         }
24052         this.checked = state;
24053         if(suppressEvent !== true){
24054             this.fireEvent('check', this, state);
24055         }
24056         this.inSetChecked = true;
24057         this.el.dom.value = state ? this.inputValue : this.valueOff;
24058         this.inSetChecked = false;
24059         
24060     },
24061     // handle setting of hidden value by some other method!!?!?
24062     setFromHidden: function()
24063     {
24064         if(!this.el){
24065             return;
24066         }
24067         //console.log("SET FROM HIDDEN");
24068         //alert('setFrom hidden');
24069         this.setValue(this.el.dom.value);
24070     },
24071     
24072     onDestroy : function()
24073     {
24074         if(this.viewEl){
24075             Roo.get(this.viewEl).remove();
24076         }
24077          
24078         Roo.form.Checkbox.superclass.onDestroy.call(this);
24079     }
24080
24081 });/*
24082  * Based on:
24083  * Ext JS Library 1.1.1
24084  * Copyright(c) 2006-2007, Ext JS, LLC.
24085  *
24086  * Originally Released Under LGPL - original licence link has changed is not relivant.
24087  *
24088  * Fork - LGPL
24089  * <script type="text/javascript">
24090  */
24091  
24092 /**
24093  * @class Roo.form.Radio
24094  * @extends Roo.form.Checkbox
24095  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
24096  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
24097  * @constructor
24098  * Creates a new Radio
24099  * @param {Object} config Configuration options
24100  */
24101 Roo.form.Radio = function(){
24102     Roo.form.Radio.superclass.constructor.apply(this, arguments);
24103 };
24104 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
24105     inputType: 'radio',
24106
24107     /**
24108      * If this radio is part of a group, it will return the selected value
24109      * @return {String}
24110      */
24111     getGroupValue : function(){
24112         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
24113     }
24114 });//<script type="text/javascript">
24115
24116 /*
24117  * Ext JS Library 1.1.1
24118  * Copyright(c) 2006-2007, Ext JS, LLC.
24119  * licensing@extjs.com
24120  * 
24121  * http://www.extjs.com/license
24122  */
24123  
24124  /*
24125   * 
24126   * Known bugs:
24127   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
24128   * - IE ? - no idea how much works there.
24129   * 
24130   * 
24131   * 
24132   */
24133  
24134
24135 /**
24136  * @class Ext.form.HtmlEditor
24137  * @extends Ext.form.Field
24138  * Provides a lightweight HTML Editor component.
24139  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
24140  * 
24141  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
24142  * supported by this editor.</b><br/><br/>
24143  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
24144  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24145  */
24146 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
24147       /**
24148      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
24149      */
24150     toolbars : false,
24151     /**
24152      * @cfg {String} createLinkText The default text for the create link prompt
24153      */
24154     createLinkText : 'Please enter the URL for the link:',
24155     /**
24156      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
24157      */
24158     defaultLinkValue : 'http:/'+'/',
24159    
24160      /**
24161      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24162      *                        Roo.resizable.
24163      */
24164     resizable : false,
24165      /**
24166      * @cfg {Number} height (in pixels)
24167      */   
24168     height: 300,
24169    /**
24170      * @cfg {Number} width (in pixels)
24171      */   
24172     width: 500,
24173     
24174     /**
24175      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24176      * 
24177      */
24178     stylesheets: false,
24179     
24180     // id of frame..
24181     frameId: false,
24182     
24183     // private properties
24184     validationEvent : false,
24185     deferHeight: true,
24186     initialized : false,
24187     activated : false,
24188     sourceEditMode : false,
24189     onFocus : Roo.emptyFn,
24190     iframePad:3,
24191     hideMode:'offsets',
24192     
24193     defaultAutoCreate : { // modified by initCompnoent..
24194         tag: "textarea",
24195         style:"width:500px;height:300px;",
24196         autocomplete: "off"
24197     },
24198
24199     // private
24200     initComponent : function(){
24201         this.addEvents({
24202             /**
24203              * @event initialize
24204              * Fires when the editor is fully initialized (including the iframe)
24205              * @param {HtmlEditor} this
24206              */
24207             initialize: true,
24208             /**
24209              * @event activate
24210              * Fires when the editor is first receives the focus. Any insertion must wait
24211              * until after this event.
24212              * @param {HtmlEditor} this
24213              */
24214             activate: true,
24215              /**
24216              * @event beforesync
24217              * Fires before the textarea is updated with content from the editor iframe. Return false
24218              * to cancel the sync.
24219              * @param {HtmlEditor} this
24220              * @param {String} html
24221              */
24222             beforesync: true,
24223              /**
24224              * @event beforepush
24225              * Fires before the iframe editor is updated with content from the textarea. Return false
24226              * to cancel the push.
24227              * @param {HtmlEditor} this
24228              * @param {String} html
24229              */
24230             beforepush: true,
24231              /**
24232              * @event sync
24233              * Fires when the textarea is updated with content from the editor iframe.
24234              * @param {HtmlEditor} this
24235              * @param {String} html
24236              */
24237             sync: true,
24238              /**
24239              * @event push
24240              * Fires when the iframe editor is updated with content from the textarea.
24241              * @param {HtmlEditor} this
24242              * @param {String} html
24243              */
24244             push: true,
24245              /**
24246              * @event editmodechange
24247              * Fires when the editor switches edit modes
24248              * @param {HtmlEditor} this
24249              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
24250              */
24251             editmodechange: true,
24252             /**
24253              * @event editorevent
24254              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24255              * @param {HtmlEditor} this
24256              */
24257             editorevent: true
24258         });
24259         this.defaultAutoCreate =  {
24260             tag: "textarea",
24261             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
24262             autocomplete: "off"
24263         };
24264     },
24265
24266     /**
24267      * Protected method that will not generally be called directly. It
24268      * is called when the editor creates its toolbar. Override this method if you need to
24269      * add custom toolbar buttons.
24270      * @param {HtmlEditor} editor
24271      */
24272     createToolbar : function(editor){
24273         if (!editor.toolbars || !editor.toolbars.length) {
24274             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
24275         }
24276         
24277         for (var i =0 ; i < editor.toolbars.length;i++) {
24278             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
24279             editor.toolbars[i].init(editor);
24280         }
24281          
24282         
24283     },
24284
24285     /**
24286      * Protected method that will not generally be called directly. It
24287      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24288      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24289      */
24290     getDocMarkup : function(){
24291         // body styles..
24292         var st = '';
24293         if (this.stylesheets === false) {
24294             
24295             Roo.get(document.head).select('style').each(function(node) {
24296                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24297             });
24298             
24299             Roo.get(document.head).select('link').each(function(node) { 
24300                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24301             });
24302             
24303         } else if (!this.stylesheets.length) {
24304                 // simple..
24305                 st = '<style type="text/css">' +
24306                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24307                    '</style>';
24308         } else {
24309             Roo.each(this.stylesheets, function(s) {
24310                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
24311             });
24312             
24313         }
24314         
24315         return '<html><head>' + st  +
24316             //<style type="text/css">' +
24317             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24318             //'</style>' +
24319             ' </head><body></body></html>';
24320     },
24321
24322     // private
24323     onRender : function(ct, position)
24324     {
24325         var _t = this;
24326         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
24327         this.el.dom.style.border = '0 none';
24328         this.el.dom.setAttribute('tabIndex', -1);
24329         this.el.addClass('x-hidden');
24330         if(Roo.isIE){ // fix IE 1px bogus margin
24331             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24332         }
24333         this.wrap = this.el.wrap({
24334             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
24335         });
24336         
24337         if (this.resizable) {
24338             this.resizeEl = new Roo.Resizable(this.wrap, {
24339                 pinned : true,
24340                 wrap: true,
24341                 dynamic : true,
24342                 minHeight : this.height,
24343                 height: this.height,
24344                 handles : this.resizable,
24345                 width: this.width,
24346                 listeners : {
24347                     resize : function(r, w, h) {
24348                         _t.onResize(w,h); // -something
24349                     }
24350                 }
24351             });
24352             
24353         }
24354
24355         this.frameId = Roo.id();
24356         
24357         this.createToolbar(this);
24358         
24359       
24360         
24361         var iframe = this.wrap.createChild({
24362             tag: 'iframe',
24363             id: this.frameId,
24364             name: this.frameId,
24365             frameBorder : 'no',
24366             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24367         }, this.el
24368         );
24369         
24370        // console.log(iframe);
24371         //this.wrap.dom.appendChild(iframe);
24372
24373         this.iframe = iframe.dom;
24374
24375          this.assignDocWin();
24376         
24377         this.doc.designMode = 'on';
24378        
24379         this.doc.open();
24380         this.doc.write(this.getDocMarkup());
24381         this.doc.close();
24382
24383         
24384         var task = { // must defer to wait for browser to be ready
24385             run : function(){
24386                 //console.log("run task?" + this.doc.readyState);
24387                 this.assignDocWin();
24388                 if(this.doc.body || this.doc.readyState == 'complete'){
24389                     try {
24390                         this.doc.designMode="on";
24391                     } catch (e) {
24392                         return;
24393                     }
24394                     Roo.TaskMgr.stop(task);
24395                     this.initEditor.defer(10, this);
24396                 }
24397             },
24398             interval : 10,
24399             duration:10000,
24400             scope: this
24401         };
24402         Roo.TaskMgr.start(task);
24403
24404         if(!this.width){
24405             this.setSize(this.wrap.getSize());
24406         }
24407         if (this.resizeEl) {
24408             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
24409             // should trigger onReize..
24410         }
24411     },
24412
24413     // private
24414     onResize : function(w, h)
24415     {
24416         //Roo.log('resize: ' +w + ',' + h );
24417         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
24418         if(this.el && this.iframe){
24419             if(typeof w == 'number'){
24420                 var aw = w - this.wrap.getFrameWidth('lr');
24421                 this.el.setWidth(this.adjustWidth('textarea', aw));
24422                 this.iframe.style.width = aw + 'px';
24423             }
24424             if(typeof h == 'number'){
24425                 var tbh = 0;
24426                 for (var i =0; i < this.toolbars.length;i++) {
24427                     // fixme - ask toolbars for heights?
24428                     tbh += this.toolbars[i].tb.el.getHeight();
24429                     if (this.toolbars[i].footer) {
24430                         tbh += this.toolbars[i].footer.el.getHeight();
24431                     }
24432                 }
24433                 
24434                 
24435                 
24436                 
24437                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24438                 ah -= 5; // knock a few pixes off for look..
24439                 this.el.setHeight(this.adjustWidth('textarea', ah));
24440                 this.iframe.style.height = ah + 'px';
24441                 if(this.doc){
24442                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
24443                 }
24444             }
24445         }
24446     },
24447
24448     /**
24449      * Toggles the editor between standard and source edit mode.
24450      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24451      */
24452     toggleSourceEdit : function(sourceEditMode){
24453         
24454         this.sourceEditMode = sourceEditMode === true;
24455         
24456         if(this.sourceEditMode){
24457           
24458             this.syncValue();
24459             this.iframe.className = 'x-hidden';
24460             this.el.removeClass('x-hidden');
24461             this.el.dom.removeAttribute('tabIndex');
24462             this.el.focus();
24463         }else{
24464              
24465             this.pushValue();
24466             this.iframe.className = '';
24467             this.el.addClass('x-hidden');
24468             this.el.dom.setAttribute('tabIndex', -1);
24469             this.deferFocus();
24470         }
24471         this.setSize(this.wrap.getSize());
24472         this.fireEvent('editmodechange', this, this.sourceEditMode);
24473     },
24474
24475     // private used internally
24476     createLink : function(){
24477         var url = prompt(this.createLinkText, this.defaultLinkValue);
24478         if(url && url != 'http:/'+'/'){
24479             this.relayCmd('createlink', url);
24480         }
24481     },
24482
24483     // private (for BoxComponent)
24484     adjustSize : Roo.BoxComponent.prototype.adjustSize,
24485
24486     // private (for BoxComponent)
24487     getResizeEl : function(){
24488         return this.wrap;
24489     },
24490
24491     // private (for BoxComponent)
24492     getPositionEl : function(){
24493         return this.wrap;
24494     },
24495
24496     // private
24497     initEvents : function(){
24498         this.originalValue = this.getValue();
24499     },
24500
24501     /**
24502      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24503      * @method
24504      */
24505     markInvalid : Roo.emptyFn,
24506     /**
24507      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24508      * @method
24509      */
24510     clearInvalid : Roo.emptyFn,
24511
24512     setValue : function(v){
24513         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
24514         this.pushValue();
24515     },
24516
24517     /**
24518      * Protected method that will not generally be called directly. If you need/want
24519      * custom HTML cleanup, this is the method you should override.
24520      * @param {String} html The HTML to be cleaned
24521      * return {String} The cleaned HTML
24522      */
24523     cleanHtml : function(html){
24524         html = String(html);
24525         if(html.length > 5){
24526             if(Roo.isSafari){ // strip safari nonsense
24527                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24528             }
24529         }
24530         if(html == '&nbsp;'){
24531             html = '';
24532         }
24533         return html;
24534     },
24535
24536     /**
24537      * Protected method that will not generally be called directly. Syncs the contents
24538      * of the editor iframe with the textarea.
24539      */
24540     syncValue : function(){
24541         if(this.initialized){
24542             var bd = (this.doc.body || this.doc.documentElement);
24543             //this.cleanUpPaste();
24544             var html = bd.innerHTML;
24545             if(Roo.isSafari){
24546                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24547                 var m = bs.match(/text-align:(.*?);/i);
24548                 if(m && m[1]){
24549                     html = '<div style="'+m[0]+'">' + html + '</div>';
24550                 }
24551             }
24552             html = this.cleanHtml(html);
24553             if(this.fireEvent('beforesync', this, html) !== false){
24554                 this.el.dom.value = html;
24555                 this.fireEvent('sync', this, html);
24556             }
24557         }
24558     },
24559
24560     /**
24561      * Protected method that will not generally be called directly. Pushes the value of the textarea
24562      * into the iframe editor.
24563      */
24564     pushValue : function(){
24565         if(this.initialized){
24566             var v = this.el.dom.value;
24567             if(v.length < 1){
24568                 v = '&#160;';
24569             }
24570             
24571             if(this.fireEvent('beforepush', this, v) !== false){
24572                 var d = (this.doc.body || this.doc.documentElement);
24573                 d.innerHTML = v;
24574                 this.cleanUpPaste();
24575                 this.el.dom.value = d.innerHTML;
24576                 this.fireEvent('push', this, v);
24577             }
24578         }
24579     },
24580
24581     // private
24582     deferFocus : function(){
24583         this.focus.defer(10, this);
24584     },
24585
24586     // doc'ed in Field
24587     focus : function(){
24588         if(this.win && !this.sourceEditMode){
24589             this.win.focus();
24590         }else{
24591             this.el.focus();
24592         }
24593     },
24594     
24595     assignDocWin: function()
24596     {
24597         var iframe = this.iframe;
24598         
24599          if(Roo.isIE){
24600             this.doc = iframe.contentWindow.document;
24601             this.win = iframe.contentWindow;
24602         } else {
24603             if (!Roo.get(this.frameId)) {
24604                 return;
24605             }
24606             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24607             this.win = Roo.get(this.frameId).dom.contentWindow;
24608         }
24609     },
24610     
24611     // private
24612     initEditor : function(){
24613         //console.log("INIT EDITOR");
24614         this.assignDocWin();
24615         
24616         
24617         
24618         this.doc.designMode="on";
24619         this.doc.open();
24620         this.doc.write(this.getDocMarkup());
24621         this.doc.close();
24622         
24623         var dbody = (this.doc.body || this.doc.documentElement);
24624         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24625         // this copies styles from the containing element into thsi one..
24626         // not sure why we need all of this..
24627         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24628         ss['background-attachment'] = 'fixed'; // w3c
24629         dbody.bgProperties = 'fixed'; // ie
24630         Roo.DomHelper.applyStyles(dbody, ss);
24631         Roo.EventManager.on(this.doc, {
24632             //'mousedown': this.onEditorEvent,
24633             'mouseup': this.onEditorEvent,
24634             'dblclick': this.onEditorEvent,
24635             'click': this.onEditorEvent,
24636             'keyup': this.onEditorEvent,
24637             buffer:100,
24638             scope: this
24639         });
24640         if(Roo.isGecko){
24641             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24642         }
24643         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24644             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24645         }
24646         this.initialized = true;
24647
24648         this.fireEvent('initialize', this);
24649         this.pushValue();
24650     },
24651
24652     // private
24653     onDestroy : function(){
24654         
24655         
24656         
24657         if(this.rendered){
24658             
24659             for (var i =0; i < this.toolbars.length;i++) {
24660                 // fixme - ask toolbars for heights?
24661                 this.toolbars[i].onDestroy();
24662             }
24663             
24664             this.wrap.dom.innerHTML = '';
24665             this.wrap.remove();
24666         }
24667     },
24668
24669     // private
24670     onFirstFocus : function(){
24671         
24672         this.assignDocWin();
24673         
24674         
24675         this.activated = true;
24676         for (var i =0; i < this.toolbars.length;i++) {
24677             this.toolbars[i].onFirstFocus();
24678         }
24679        
24680         if(Roo.isGecko){ // prevent silly gecko errors
24681             this.win.focus();
24682             var s = this.win.getSelection();
24683             if(!s.focusNode || s.focusNode.nodeType != 3){
24684                 var r = s.getRangeAt(0);
24685                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24686                 r.collapse(true);
24687                 this.deferFocus();
24688             }
24689             try{
24690                 this.execCmd('useCSS', true);
24691                 this.execCmd('styleWithCSS', false);
24692             }catch(e){}
24693         }
24694         this.fireEvent('activate', this);
24695     },
24696
24697     // private
24698     adjustFont: function(btn){
24699         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24700         //if(Roo.isSafari){ // safari
24701         //    adjust *= 2;
24702        // }
24703         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24704         if(Roo.isSafari){ // safari
24705             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24706             v =  (v < 10) ? 10 : v;
24707             v =  (v > 48) ? 48 : v;
24708             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24709             
24710         }
24711         
24712         
24713         v = Math.max(1, v+adjust);
24714         
24715         this.execCmd('FontSize', v  );
24716     },
24717
24718     onEditorEvent : function(e){
24719         this.fireEvent('editorevent', this, e);
24720       //  this.updateToolbar();
24721         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24722     },
24723
24724     insertTag : function(tg)
24725     {
24726         // could be a bit smarter... -> wrap the current selected tRoo..
24727         
24728         this.execCmd("formatblock",   tg);
24729         
24730     },
24731     
24732     insertText : function(txt)
24733     {
24734         
24735         
24736         range = this.createRange();
24737         range.deleteContents();
24738                //alert(Sender.getAttribute('label'));
24739                
24740         range.insertNode(this.doc.createTextNode(txt));
24741     } ,
24742     
24743     // private
24744     relayBtnCmd : function(btn){
24745         this.relayCmd(btn.cmd);
24746     },
24747
24748     /**
24749      * Executes a Midas editor command on the editor document and performs necessary focus and
24750      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24751      * @param {String} cmd The Midas command
24752      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24753      */
24754     relayCmd : function(cmd, value){
24755         this.win.focus();
24756         this.execCmd(cmd, value);
24757         this.fireEvent('editorevent', this);
24758         //this.updateToolbar();
24759         this.deferFocus();
24760     },
24761
24762     /**
24763      * Executes a Midas editor command directly on the editor document.
24764      * For visual commands, you should use {@link #relayCmd} instead.
24765      * <b>This should only be called after the editor is initialized.</b>
24766      * @param {String} cmd The Midas command
24767      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24768      */
24769     execCmd : function(cmd, value){
24770         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24771         this.syncValue();
24772     },
24773
24774    
24775     /**
24776      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24777      * to insert tRoo.
24778      * @param {String} text
24779      */
24780     insertAtCursor : function(text){
24781         if(!this.activated){
24782             return;
24783         }
24784         if(Roo.isIE){
24785             this.win.focus();
24786             var r = this.doc.selection.createRange();
24787             if(r){
24788                 r.collapse(true);
24789                 r.pasteHTML(text);
24790                 this.syncValue();
24791                 this.deferFocus();
24792             }
24793         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24794             this.win.focus();
24795             this.execCmd('InsertHTML', text);
24796             this.deferFocus();
24797         }
24798     },
24799  // private
24800     mozKeyPress : function(e){
24801         if(e.ctrlKey){
24802             var c = e.getCharCode(), cmd;
24803           
24804             if(c > 0){
24805                 c = String.fromCharCode(c).toLowerCase();
24806                 switch(c){
24807                     case 'b':
24808                         cmd = 'bold';
24809                     break;
24810                     case 'i':
24811                         cmd = 'italic';
24812                     break;
24813                     case 'u':
24814                         cmd = 'underline';
24815                         break;
24816                     case 'v':
24817                         this.cleanUpPaste.defer(100, this);
24818                         return;
24819                     break;
24820                 }
24821                 if(cmd){
24822                     this.win.focus();
24823                     this.execCmd(cmd);
24824                     this.deferFocus();
24825                     e.preventDefault();
24826                 }
24827                 
24828             }
24829         }
24830     },
24831
24832     // private
24833     fixKeys : function(){ // load time branching for fastest keydown performance
24834         if(Roo.isIE){
24835             return function(e){
24836                 var k = e.getKey(), r;
24837                 if(k == e.TAB){
24838                     e.stopEvent();
24839                     r = this.doc.selection.createRange();
24840                     if(r){
24841                         r.collapse(true);
24842                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24843                         this.deferFocus();
24844                     }
24845                     return;
24846                 }
24847                 
24848                 if(k == e.ENTER){
24849                     r = this.doc.selection.createRange();
24850                     if(r){
24851                         var target = r.parentElement();
24852                         if(!target || target.tagName.toLowerCase() != 'li'){
24853                             e.stopEvent();
24854                             r.pasteHTML('<br />');
24855                             r.collapse(false);
24856                             r.select();
24857                         }
24858                     }
24859                 }
24860                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24861                     this.cleanUpPaste.defer(100, this);
24862                     return;
24863                 }
24864                 
24865                 
24866             };
24867         }else if(Roo.isOpera){
24868             return function(e){
24869                 var k = e.getKey();
24870                 if(k == e.TAB){
24871                     e.stopEvent();
24872                     this.win.focus();
24873                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24874                     this.deferFocus();
24875                 }
24876                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24877                     this.cleanUpPaste.defer(100, this);
24878                     return;
24879                 }
24880                 
24881             };
24882         }else if(Roo.isSafari){
24883             return function(e){
24884                 var k = e.getKey();
24885                 
24886                 if(k == e.TAB){
24887                     e.stopEvent();
24888                     this.execCmd('InsertText','\t');
24889                     this.deferFocus();
24890                     return;
24891                 }
24892                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24893                     this.cleanUpPaste.defer(100, this);
24894                     return;
24895                 }
24896                 
24897              };
24898         }
24899     }(),
24900     
24901     getAllAncestors: function()
24902     {
24903         var p = this.getSelectedNode();
24904         var a = [];
24905         if (!p) {
24906             a.push(p); // push blank onto stack..
24907             p = this.getParentElement();
24908         }
24909         
24910         
24911         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24912             a.push(p);
24913             p = p.parentNode;
24914         }
24915         a.push(this.doc.body);
24916         return a;
24917     },
24918     lastSel : false,
24919     lastSelNode : false,
24920     
24921     
24922     getSelection : function() 
24923     {
24924         this.assignDocWin();
24925         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24926     },
24927     
24928     getSelectedNode: function() 
24929     {
24930         // this may only work on Gecko!!!
24931         
24932         // should we cache this!!!!
24933         
24934         
24935         
24936          
24937         var range = this.createRange(this.getSelection()).cloneRange();
24938         
24939         if (Roo.isIE) {
24940             var parent = range.parentElement();
24941             while (true) {
24942                 var testRange = range.duplicate();
24943                 testRange.moveToElementText(parent);
24944                 if (testRange.inRange(range)) {
24945                     break;
24946                 }
24947                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24948                     break;
24949                 }
24950                 parent = parent.parentElement;
24951             }
24952             return parent;
24953         }
24954         
24955         // is ancestor a text element.
24956         var ac =  range.commonAncestorContainer;
24957         if (ac.nodeType == 3) {
24958             ac = ac.parentNode;
24959         }
24960         
24961         var ar = ac.childNodes;
24962          
24963         var nodes = [];
24964         var other_nodes = [];
24965         var has_other_nodes = false;
24966         for (var i=0;i<ar.length;i++) {
24967             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24968                 continue;
24969             }
24970             // fullly contained node.
24971             
24972             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24973                 nodes.push(ar[i]);
24974                 continue;
24975             }
24976             
24977             // probably selected..
24978             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24979                 other_nodes.push(ar[i]);
24980                 continue;
24981             }
24982             // outer..
24983             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24984                 continue;
24985             }
24986             
24987             
24988             has_other_nodes = true;
24989         }
24990         if (!nodes.length && other_nodes.length) {
24991             nodes= other_nodes;
24992         }
24993         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24994             return false;
24995         }
24996         
24997         return nodes[0];
24998     },
24999     createRange: function(sel)
25000     {
25001         // this has strange effects when using with 
25002         // top toolbar - not sure if it's a great idea.
25003         //this.editor.contentWindow.focus();
25004         if (typeof sel != "undefined") {
25005             try {
25006                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25007             } catch(e) {
25008                 return this.doc.createRange();
25009             }
25010         } else {
25011             return this.doc.createRange();
25012         }
25013     },
25014     getParentElement: function()
25015     {
25016         
25017         this.assignDocWin();
25018         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25019         
25020         var range = this.createRange(sel);
25021          
25022         try {
25023             var p = range.commonAncestorContainer;
25024             while (p.nodeType == 3) { // text node
25025                 p = p.parentNode;
25026             }
25027             return p;
25028         } catch (e) {
25029             return null;
25030         }
25031     
25032     },
25033     /***
25034      *
25035      * Range intersection.. the hard stuff...
25036      *  '-1' = before
25037      *  '0' = hits..
25038      *  '1' = after.
25039      *         [ -- selected range --- ]
25040      *   [fail]                        [fail]
25041      *
25042      *    basically..
25043      *      if end is before start or  hits it. fail.
25044      *      if start is after end or hits it fail.
25045      *
25046      *   if either hits (but other is outside. - then it's not 
25047      *   
25048      *    
25049      **/
25050     
25051     
25052     // @see http://www.thismuchiknow.co.uk/?p=64.
25053     rangeIntersectsNode : function(range, node)
25054     {
25055         var nodeRange = node.ownerDocument.createRange();
25056         try {
25057             nodeRange.selectNode(node);
25058         } catch (e) {
25059             nodeRange.selectNodeContents(node);
25060         }
25061     
25062         var rangeStartRange = range.cloneRange();
25063         rangeStartRange.collapse(true);
25064     
25065         var rangeEndRange = range.cloneRange();
25066         rangeEndRange.collapse(false);
25067     
25068         var nodeStartRange = nodeRange.cloneRange();
25069         nodeStartRange.collapse(true);
25070     
25071         var nodeEndRange = nodeRange.cloneRange();
25072         nodeEndRange.collapse(false);
25073     
25074         return rangeStartRange.compareBoundaryPoints(
25075                  Range.START_TO_START, nodeEndRange) == -1 &&
25076                rangeEndRange.compareBoundaryPoints(
25077                  Range.START_TO_START, nodeStartRange) == 1;
25078         
25079          
25080     },
25081     rangeCompareNode : function(range, node)
25082     {
25083         var nodeRange = node.ownerDocument.createRange();
25084         try {
25085             nodeRange.selectNode(node);
25086         } catch (e) {
25087             nodeRange.selectNodeContents(node);
25088         }
25089         
25090         
25091         range.collapse(true);
25092     
25093         nodeRange.collapse(true);
25094      
25095         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25096         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25097          
25098         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25099         
25100         var nodeIsBefore   =  ss == 1;
25101         var nodeIsAfter    = ee == -1;
25102         
25103         if (nodeIsBefore && nodeIsAfter)
25104             return 0; // outer
25105         if (!nodeIsBefore && nodeIsAfter)
25106             return 1; //right trailed.
25107         
25108         if (nodeIsBefore && !nodeIsAfter)
25109             return 2;  // left trailed.
25110         // fully contined.
25111         return 3;
25112     },
25113
25114     // private? - in a new class?
25115     cleanUpPaste :  function()
25116     {
25117         // cleans up the whole document..
25118          Roo.log('cleanuppaste');
25119         this.cleanUpChildren(this.doc.body);
25120         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25121         if (clean != this.doc.body.innerHTML) {
25122             this.doc.body.innerHTML = clean;
25123         }
25124         
25125     },
25126     
25127     cleanWordChars : function(input) {
25128         var he = Roo.form.HtmlEditor;
25129     
25130         var output = input;
25131         Roo.each(he.swapCodes, function(sw) { 
25132         
25133             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25134             output = output.replace(swapper, sw[1]);
25135         });
25136         return output;
25137     },
25138     
25139     
25140     cleanUpChildren : function (n)
25141     {
25142         if (!n.childNodes.length) {
25143             return;
25144         }
25145         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25146            this.cleanUpChild(n.childNodes[i]);
25147         }
25148     },
25149     
25150     
25151         
25152     
25153     cleanUpChild : function (node)
25154     {
25155         //console.log(node);
25156         if (node.nodeName == "#text") {
25157             // clean up silly Windows -- stuff?
25158             return; 
25159         }
25160         if (node.nodeName == "#comment") {
25161             node.parentNode.removeChild(node);
25162             // clean up silly Windows -- stuff?
25163             return; 
25164         }
25165         
25166         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
25167             // remove node.
25168             node.parentNode.removeChild(node);
25169             return;
25170             
25171         }
25172         
25173         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
25174         
25175         // remove <a name=....> as rendering on yahoo mailer is bored with this.
25176         
25177         if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25178             remove_keep_children = true;
25179         }
25180         
25181         if (remove_keep_children) {
25182             this.cleanUpChildren(node);
25183             // inserts everything just before this node...
25184             while (node.childNodes.length) {
25185                 var cn = node.childNodes[0];
25186                 node.removeChild(cn);
25187                 node.parentNode.insertBefore(cn, node);
25188             }
25189             node.parentNode.removeChild(node);
25190             return;
25191         }
25192         
25193         if (!node.attributes || !node.attributes.length) {
25194             this.cleanUpChildren(node);
25195             return;
25196         }
25197         
25198         function cleanAttr(n,v)
25199         {
25200             
25201             if (v.match(/^\./) || v.match(/^\//)) {
25202                 return;
25203             }
25204             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
25205                 return;
25206             }
25207             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
25208             node.removeAttribute(n);
25209             
25210         }
25211         
25212         function cleanStyle(n,v)
25213         {
25214             if (v.match(/expression/)) { //XSS?? should we even bother..
25215                 node.removeAttribute(n);
25216                 return;
25217             }
25218             
25219             
25220             var parts = v.split(/;/);
25221             Roo.each(parts, function(p) {
25222                 p = p.replace(/\s+/g,'');
25223                 if (!p.length) {
25224                     return true;
25225                 }
25226                 var l = p.split(':').shift().replace(/\s+/g,'');
25227                 
25228                 // only allow 'c whitelisted system attributes'
25229                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
25230                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
25231                     node.removeAttribute(n);
25232                     return false;
25233                 }
25234                 return true;
25235             });
25236             
25237             
25238         }
25239         
25240         
25241         for (var i = node.attributes.length-1; i > -1 ; i--) {
25242             var a = node.attributes[i];
25243             //console.log(a);
25244             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
25245                 node.removeAttribute(a.name);
25246                 return;
25247             }
25248             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
25249                 cleanAttr(a.name,a.value); // fixme..
25250                 return;
25251             }
25252             if (a.name == 'style') {
25253                 cleanStyle(a.name,a.value);
25254             }
25255             /// clean up MS crap..
25256             // tecnically this should be a list of valid class'es..
25257             
25258             
25259             if (a.name == 'class') {
25260                 if (a.value.match(/^Mso/)) {
25261                     node.className = '';
25262                 }
25263                 
25264                 if (a.value.match(/body/)) {
25265                     node.className = '';
25266                 }
25267             }
25268             
25269             // style cleanup!?
25270             // class cleanup?
25271             
25272         }
25273         
25274         
25275         this.cleanUpChildren(node);
25276         
25277         
25278     }
25279     
25280     
25281     // hide stuff that is not compatible
25282     /**
25283      * @event blur
25284      * @hide
25285      */
25286     /**
25287      * @event change
25288      * @hide
25289      */
25290     /**
25291      * @event focus
25292      * @hide
25293      */
25294     /**
25295      * @event specialkey
25296      * @hide
25297      */
25298     /**
25299      * @cfg {String} fieldClass @hide
25300      */
25301     /**
25302      * @cfg {String} focusClass @hide
25303      */
25304     /**
25305      * @cfg {String} autoCreate @hide
25306      */
25307     /**
25308      * @cfg {String} inputType @hide
25309      */
25310     /**
25311      * @cfg {String} invalidClass @hide
25312      */
25313     /**
25314      * @cfg {String} invalidText @hide
25315      */
25316     /**
25317      * @cfg {String} msgFx @hide
25318      */
25319     /**
25320      * @cfg {String} validateOnBlur @hide
25321      */
25322 });
25323
25324 Roo.form.HtmlEditor.white = [
25325         'area', 'br', 'img', 'input', 'hr', 'wbr',
25326         
25327        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25328        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25329        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25330        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25331        'table',   'ul',         'xmp', 
25332        
25333        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25334       'thead',   'tr', 
25335      
25336       'dir', 'menu', 'ol', 'ul', 'dl',
25337        
25338       'embed',  'object'
25339 ];
25340
25341
25342 Roo.form.HtmlEditor.black = [
25343     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25344         'applet', // 
25345         'base',   'basefont', 'bgsound', 'blink',  'body', 
25346         'frame',  'frameset', 'head',    'html',   'ilayer', 
25347         'iframe', 'layer',  'link',     'meta',    'object',   
25348         'script', 'style' ,'title',  'xml' // clean later..
25349 ];
25350 Roo.form.HtmlEditor.clean = [
25351     'script', 'style', 'title', 'xml'
25352 ];
25353 Roo.form.HtmlEditor.remove = [
25354     'font'
25355 ];
25356 // attributes..
25357
25358 Roo.form.HtmlEditor.ablack = [
25359     'on'
25360 ];
25361     
25362 Roo.form.HtmlEditor.aclean = [ 
25363     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25364 ];
25365
25366 // protocols..
25367 Roo.form.HtmlEditor.pwhite= [
25368         'http',  'https',  'mailto'
25369 ];
25370
25371 // white listed style attributes.
25372 Roo.form.HtmlEditor.cwhite= [
25373         'text-align',
25374         'font-size'
25375 ];
25376
25377
25378 Roo.form.HtmlEditor.swapCodes   =[ 
25379     [    8211, "--" ], 
25380     [    8212, "--" ], 
25381     [    8216,  "'" ],  
25382     [    8217, "'" ],  
25383     [    8220, '"' ],  
25384     [    8221, '"' ],  
25385     [    8226, "*" ],  
25386     [    8230, "..." ]
25387 ]; 
25388
25389     // <script type="text/javascript">
25390 /*
25391  * Based on
25392  * Ext JS Library 1.1.1
25393  * Copyright(c) 2006-2007, Ext JS, LLC.
25394  *  
25395  
25396  */
25397
25398 /**
25399  * @class Roo.form.HtmlEditorToolbar1
25400  * Basic Toolbar
25401  * 
25402  * Usage:
25403  *
25404  new Roo.form.HtmlEditor({
25405     ....
25406     toolbars : [
25407         new Roo.form.HtmlEditorToolbar1({
25408             disable : { fonts: 1 , format: 1, ..., ... , ...],
25409             btns : [ .... ]
25410         })
25411     }
25412      
25413  * 
25414  * @cfg {Object} disable List of elements to disable..
25415  * @cfg {Array} btns List of additional buttons.
25416  * 
25417  * 
25418  * NEEDS Extra CSS? 
25419  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25420  */
25421  
25422 Roo.form.HtmlEditor.ToolbarStandard = function(config)
25423 {
25424     
25425     Roo.apply(this, config);
25426     
25427     // default disabled, based on 'good practice'..
25428     this.disable = this.disable || {};
25429     Roo.applyIf(this.disable, {
25430         fontSize : true,
25431         colors : true,
25432         specialElements : true
25433     });
25434     
25435     
25436     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25437     // dont call parent... till later.
25438 }
25439
25440 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
25441     
25442     tb: false,
25443     
25444     rendered: false,
25445     
25446     editor : false,
25447     /**
25448      * @cfg {Object} disable  List of toolbar elements to disable
25449          
25450      */
25451     disable : false,
25452       /**
25453      * @cfg {Array} fontFamilies An array of available font families
25454      */
25455     fontFamilies : [
25456         'Arial',
25457         'Courier New',
25458         'Tahoma',
25459         'Times New Roman',
25460         'Verdana'
25461     ],
25462     
25463     specialChars : [
25464            "&#169;",
25465           "&#174;",     
25466           "&#8482;",    
25467           "&#163;" ,    
25468          // "&#8212;",    
25469           "&#8230;",    
25470           "&#247;" ,    
25471         //  "&#225;" ,     ?? a acute?
25472            "&#8364;"    , //Euro
25473        //   "&#8220;"    ,
25474         //  "&#8221;"    ,
25475         //  "&#8226;"    ,
25476           "&#176;"  //   , // degrees
25477
25478          // "&#233;"     , // e ecute
25479          // "&#250;"     , // u ecute?
25480     ],
25481     
25482     specialElements : [
25483         {
25484             text: "Insert Table",
25485             xtype: 'MenuItem',
25486             xns : Roo.Menu,
25487             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
25488                 
25489         },
25490         {    
25491             text: "Insert Image",
25492             xtype: 'MenuItem',
25493             xns : Roo.Menu,
25494             ihtml : '<img src="about:blank"/>'
25495             
25496         }
25497         
25498          
25499     ],
25500     
25501     
25502     inputElements : [ 
25503             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
25504             "input:submit", "input:button", "select", "textarea", "label" ],
25505     formats : [
25506         ["p"] ,  
25507         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
25508         ["pre"],[ "code"], 
25509         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
25510     ],
25511      /**
25512      * @cfg {String} defaultFont default font to use.
25513      */
25514     defaultFont: 'tahoma',
25515    
25516     fontSelect : false,
25517     
25518     
25519     formatCombo : false,
25520     
25521     init : function(editor)
25522     {
25523         this.editor = editor;
25524         
25525         
25526         var fid = editor.frameId;
25527         var etb = this;
25528         function btn(id, toggle, handler){
25529             var xid = fid + '-'+ id ;
25530             return {
25531                 id : xid,
25532                 cmd : id,
25533                 cls : 'x-btn-icon x-edit-'+id,
25534                 enableToggle:toggle !== false,
25535                 scope: editor, // was editor...
25536                 handler:handler||editor.relayBtnCmd,
25537                 clickEvent:'mousedown',
25538                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
25539                 tabIndex:-1
25540             };
25541         }
25542         
25543         
25544         
25545         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
25546         this.tb = tb;
25547          // stop form submits
25548         tb.el.on('click', function(e){
25549             e.preventDefault(); // what does this do?
25550         });
25551
25552         if(!this.disable.font && !Roo.isSafari){
25553             /* why no safari for fonts
25554             editor.fontSelect = tb.el.createChild({
25555                 tag:'select',
25556                 tabIndex: -1,
25557                 cls:'x-font-select',
25558                 html: editor.createFontOptions()
25559             });
25560             editor.fontSelect.on('change', function(){
25561                 var font = editor.fontSelect.dom.value;
25562                 editor.relayCmd('fontname', font);
25563                 editor.deferFocus();
25564             }, editor);
25565             tb.add(
25566                 editor.fontSelect.dom,
25567                 '-'
25568             );
25569             */
25570         };
25571         if(!this.disable.formats){
25572             this.formatCombo = new Roo.form.ComboBox({
25573                 store: new Roo.data.SimpleStore({
25574                     id : 'tag',
25575                     fields: ['tag'],
25576                     data : this.formats // from states.js
25577                 }),
25578                 blockFocus : true,
25579                 //autoCreate : {tag: "div",  size: "20"},
25580                 displayField:'tag',
25581                 typeAhead: false,
25582                 mode: 'local',
25583                 editable : false,
25584                 triggerAction: 'all',
25585                 emptyText:'Add tag',
25586                 selectOnFocus:true,
25587                 width:135,
25588                 listeners : {
25589                     'select': function(c, r, i) {
25590                         editor.insertTag(r.get('tag'));
25591                         editor.focus();
25592                     }
25593                 }
25594
25595             });
25596             tb.addField(this.formatCombo);
25597             
25598         }
25599         
25600         if(!this.disable.format){
25601             tb.add(
25602                 btn('bold'),
25603                 btn('italic'),
25604                 btn('underline')
25605             );
25606         };
25607         if(!this.disable.fontSize){
25608             tb.add(
25609                 '-',
25610                 
25611                 
25612                 btn('increasefontsize', false, editor.adjustFont),
25613                 btn('decreasefontsize', false, editor.adjustFont)
25614             );
25615         };
25616         
25617         
25618         if(!this.disable.colors){
25619             tb.add(
25620                 '-', {
25621                     id:editor.frameId +'-forecolor',
25622                     cls:'x-btn-icon x-edit-forecolor',
25623                     clickEvent:'mousedown',
25624                     tooltip: this.buttonTips['forecolor'] || undefined,
25625                     tabIndex:-1,
25626                     menu : new Roo.menu.ColorMenu({
25627                         allowReselect: true,
25628                         focus: Roo.emptyFn,
25629                         value:'000000',
25630                         plain:true,
25631                         selectHandler: function(cp, color){
25632                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
25633                             editor.deferFocus();
25634                         },
25635                         scope: editor,
25636                         clickEvent:'mousedown'
25637                     })
25638                 }, {
25639                     id:editor.frameId +'backcolor',
25640                     cls:'x-btn-icon x-edit-backcolor',
25641                     clickEvent:'mousedown',
25642                     tooltip: this.buttonTips['backcolor'] || undefined,
25643                     tabIndex:-1,
25644                     menu : new Roo.menu.ColorMenu({
25645                         focus: Roo.emptyFn,
25646                         value:'FFFFFF',
25647                         plain:true,
25648                         allowReselect: true,
25649                         selectHandler: function(cp, color){
25650                             if(Roo.isGecko){
25651                                 editor.execCmd('useCSS', false);
25652                                 editor.execCmd('hilitecolor', color);
25653                                 editor.execCmd('useCSS', true);
25654                                 editor.deferFocus();
25655                             }else{
25656                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
25657                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
25658                                 editor.deferFocus();
25659                             }
25660                         },
25661                         scope:editor,
25662                         clickEvent:'mousedown'
25663                     })
25664                 }
25665             );
25666         };
25667         // now add all the items...
25668         
25669
25670         if(!this.disable.alignments){
25671             tb.add(
25672                 '-',
25673                 btn('justifyleft'),
25674                 btn('justifycenter'),
25675                 btn('justifyright')
25676             );
25677         };
25678
25679         //if(!Roo.isSafari){
25680             if(!this.disable.links){
25681                 tb.add(
25682                     '-',
25683                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
25684                 );
25685             };
25686
25687             if(!this.disable.lists){
25688                 tb.add(
25689                     '-',
25690                     btn('insertorderedlist'),
25691                     btn('insertunorderedlist')
25692                 );
25693             }
25694             if(!this.disable.sourceEdit){
25695                 tb.add(
25696                     '-',
25697                     btn('sourceedit', true, function(btn){
25698                         this.toggleSourceEdit(btn.pressed);
25699                     })
25700                 );
25701             }
25702         //}
25703         
25704         var smenu = { };
25705         // special menu.. - needs to be tidied up..
25706         if (!this.disable.special) {
25707             smenu = {
25708                 text: "&#169;",
25709                 cls: 'x-edit-none',
25710                 
25711                 menu : {
25712                     items : []
25713                 }
25714             };
25715             for (var i =0; i < this.specialChars.length; i++) {
25716                 smenu.menu.items.push({
25717                     
25718                     html: this.specialChars[i],
25719                     handler: function(a,b) {
25720                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
25721                         
25722                     },
25723                     tabIndex:-1
25724                 });
25725             }
25726             
25727             
25728             tb.add(smenu);
25729             
25730             
25731         }
25732          
25733         if (!this.disable.specialElements) {
25734             var semenu = {
25735                 text: "Other;",
25736                 cls: 'x-edit-none',
25737                 menu : {
25738                     items : []
25739                 }
25740             };
25741             for (var i =0; i < this.specialElements.length; i++) {
25742                 semenu.menu.items.push(
25743                     Roo.apply({ 
25744                         handler: function(a,b) {
25745                             editor.insertAtCursor(this.ihtml);
25746                         }
25747                     }, this.specialElements[i])
25748                 );
25749                     
25750             }
25751             
25752             tb.add(semenu);
25753             
25754             
25755         }
25756          
25757         
25758         if (this.btns) {
25759             for(var i =0; i< this.btns.length;i++) {
25760                 var b = this.btns[i];
25761                 b.cls =  'x-edit-none';
25762                 b.scope = editor;
25763                 tb.add(b);
25764             }
25765         
25766         }
25767         
25768         
25769         
25770         // disable everything...
25771         
25772         this.tb.items.each(function(item){
25773            if(item.id != editor.frameId+ '-sourceedit'){
25774                 item.disable();
25775             }
25776         });
25777         this.rendered = true;
25778         
25779         // the all the btns;
25780         editor.on('editorevent', this.updateToolbar, this);
25781         // other toolbars need to implement this..
25782         //editor.on('editmodechange', this.updateToolbar, this);
25783     },
25784     
25785     
25786     
25787     /**
25788      * Protected method that will not generally be called directly. It triggers
25789      * a toolbar update by reading the markup state of the current selection in the editor.
25790      */
25791     updateToolbar: function(){
25792
25793         if(!this.editor.activated){
25794             this.editor.onFirstFocus();
25795             return;
25796         }
25797
25798         var btns = this.tb.items.map, 
25799             doc = this.editor.doc,
25800             frameId = this.editor.frameId;
25801
25802         if(!this.disable.font && !Roo.isSafari){
25803             /*
25804             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
25805             if(name != this.fontSelect.dom.value){
25806                 this.fontSelect.dom.value = name;
25807             }
25808             */
25809         }
25810         if(!this.disable.format){
25811             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
25812             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
25813             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
25814         }
25815         if(!this.disable.alignments){
25816             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
25817             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
25818             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
25819         }
25820         if(!Roo.isSafari && !this.disable.lists){
25821             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
25822             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
25823         }
25824         
25825         var ans = this.editor.getAllAncestors();
25826         if (this.formatCombo) {
25827             
25828             
25829             var store = this.formatCombo.store;
25830             this.formatCombo.setValue("");
25831             for (var i =0; i < ans.length;i++) {
25832                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
25833                     // select it..
25834                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
25835                     break;
25836                 }
25837             }
25838         }
25839         
25840         
25841         
25842         // hides menus... - so this cant be on a menu...
25843         Roo.menu.MenuMgr.hideAll();
25844
25845         //this.editorsyncValue();
25846     },
25847    
25848     
25849     createFontOptions : function(){
25850         var buf = [], fs = this.fontFamilies, ff, lc;
25851         for(var i = 0, len = fs.length; i< len; i++){
25852             ff = fs[i];
25853             lc = ff.toLowerCase();
25854             buf.push(
25855                 '<option value="',lc,'" style="font-family:',ff,';"',
25856                     (this.defaultFont == lc ? ' selected="true">' : '>'),
25857                     ff,
25858                 '</option>'
25859             );
25860         }
25861         return buf.join('');
25862     },
25863     
25864     toggleSourceEdit : function(sourceEditMode){
25865         if(sourceEditMode === undefined){
25866             sourceEditMode = !this.sourceEditMode;
25867         }
25868         this.sourceEditMode = sourceEditMode === true;
25869         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
25870         // just toggle the button?
25871         if(btn.pressed !== this.editor.sourceEditMode){
25872             btn.toggle(this.editor.sourceEditMode);
25873             return;
25874         }
25875         
25876         if(this.sourceEditMode){
25877             this.tb.items.each(function(item){
25878                 if(item.cmd != 'sourceedit'){
25879                     item.disable();
25880                 }
25881             });
25882           
25883         }else{
25884             if(this.initialized){
25885                 this.tb.items.each(function(item){
25886                     item.enable();
25887                 });
25888             }
25889             
25890         }
25891         // tell the editor that it's been pressed..
25892         this.editor.toggleSourceEdit(sourceEditMode);
25893        
25894     },
25895      /**
25896      * Object collection of toolbar tooltips for the buttons in the editor. The key
25897      * is the command id associated with that button and the value is a valid QuickTips object.
25898      * For example:
25899 <pre><code>
25900 {
25901     bold : {
25902         title: 'Bold (Ctrl+B)',
25903         text: 'Make the selected text bold.',
25904         cls: 'x-html-editor-tip'
25905     },
25906     italic : {
25907         title: 'Italic (Ctrl+I)',
25908         text: 'Make the selected text italic.',
25909         cls: 'x-html-editor-tip'
25910     },
25911     ...
25912 </code></pre>
25913     * @type Object
25914      */
25915     buttonTips : {
25916         bold : {
25917             title: 'Bold (Ctrl+B)',
25918             text: 'Make the selected text bold.',
25919             cls: 'x-html-editor-tip'
25920         },
25921         italic : {
25922             title: 'Italic (Ctrl+I)',
25923             text: 'Make the selected text italic.',
25924             cls: 'x-html-editor-tip'
25925         },
25926         underline : {
25927             title: 'Underline (Ctrl+U)',
25928             text: 'Underline the selected text.',
25929             cls: 'x-html-editor-tip'
25930         },
25931         increasefontsize : {
25932             title: 'Grow Text',
25933             text: 'Increase the font size.',
25934             cls: 'x-html-editor-tip'
25935         },
25936         decreasefontsize : {
25937             title: 'Shrink Text',
25938             text: 'Decrease the font size.',
25939             cls: 'x-html-editor-tip'
25940         },
25941         backcolor : {
25942             title: 'Text Highlight Color',
25943             text: 'Change the background color of the selected text.',
25944             cls: 'x-html-editor-tip'
25945         },
25946         forecolor : {
25947             title: 'Font Color',
25948             text: 'Change the color of the selected text.',
25949             cls: 'x-html-editor-tip'
25950         },
25951         justifyleft : {
25952             title: 'Align Text Left',
25953             text: 'Align text to the left.',
25954             cls: 'x-html-editor-tip'
25955         },
25956         justifycenter : {
25957             title: 'Center Text',
25958             text: 'Center text in the editor.',
25959             cls: 'x-html-editor-tip'
25960         },
25961         justifyright : {
25962             title: 'Align Text Right',
25963             text: 'Align text to the right.',
25964             cls: 'x-html-editor-tip'
25965         },
25966         insertunorderedlist : {
25967             title: 'Bullet List',
25968             text: 'Start a bulleted list.',
25969             cls: 'x-html-editor-tip'
25970         },
25971         insertorderedlist : {
25972             title: 'Numbered List',
25973             text: 'Start a numbered list.',
25974             cls: 'x-html-editor-tip'
25975         },
25976         createlink : {
25977             title: 'Hyperlink',
25978             text: 'Make the selected text a hyperlink.',
25979             cls: 'x-html-editor-tip'
25980         },
25981         sourceedit : {
25982             title: 'Source Edit',
25983             text: 'Switch to source editing mode.',
25984             cls: 'x-html-editor-tip'
25985         }
25986     },
25987     // private
25988     onDestroy : function(){
25989         if(this.rendered){
25990             
25991             this.tb.items.each(function(item){
25992                 if(item.menu){
25993                     item.menu.removeAll();
25994                     if(item.menu.el){
25995                         item.menu.el.destroy();
25996                     }
25997                 }
25998                 item.destroy();
25999             });
26000              
26001         }
26002     },
26003     onFirstFocus: function() {
26004         this.tb.items.each(function(item){
26005            item.enable();
26006         });
26007     }
26008 });
26009
26010
26011
26012
26013 // <script type="text/javascript">
26014 /*
26015  * Based on
26016  * Ext JS Library 1.1.1
26017  * Copyright(c) 2006-2007, Ext JS, LLC.
26018  *  
26019  
26020  */
26021
26022  
26023 /**
26024  * @class Roo.form.HtmlEditor.ToolbarContext
26025  * Context Toolbar
26026  * 
26027  * Usage:
26028  *
26029  new Roo.form.HtmlEditor({
26030     ....
26031     toolbars : [
26032         { xtype: 'ToolbarStandard', styles : {} }
26033         { xtype: 'ToolbarContext', disable : {} }
26034     ]
26035 })
26036
26037      
26038  * 
26039  * @config : {Object} disable List of elements to disable.. (not done yet.)
26040  * @config : {Object} styles  Map of styles available.
26041  * 
26042  */
26043
26044 Roo.form.HtmlEditor.ToolbarContext = function(config)
26045 {
26046     
26047     Roo.apply(this, config);
26048     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26049     // dont call parent... till later.
26050     this.styles = this.styles || {};
26051 }
26052 Roo.form.HtmlEditor.ToolbarContext.types = {
26053     'IMG' : {
26054         width : {
26055             title: "Width",
26056             width: 40
26057         },
26058         height:  {
26059             title: "Height",
26060             width: 40
26061         },
26062         align: {
26063             title: "Align",
26064             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
26065             width : 80
26066             
26067         },
26068         border: {
26069             title: "Border",
26070             width: 40
26071         },
26072         alt: {
26073             title: "Alt",
26074             width: 120
26075         },
26076         src : {
26077             title: "Src",
26078             width: 220
26079         }
26080         
26081     },
26082     'A' : {
26083         name : {
26084             title: "Name",
26085             width: 50
26086         },
26087         href:  {
26088             title: "Href",
26089             width: 220
26090         } // border?
26091         
26092     },
26093     'TABLE' : {
26094         rows : {
26095             title: "Rows",
26096             width: 20
26097         },
26098         cols : {
26099             title: "Cols",
26100             width: 20
26101         },
26102         width : {
26103             title: "Width",
26104             width: 40
26105         },
26106         height : {
26107             title: "Height",
26108             width: 40
26109         },
26110         border : {
26111             title: "Border",
26112             width: 20
26113         }
26114     },
26115     'TD' : {
26116         width : {
26117             title: "Width",
26118             width: 40
26119         },
26120         height : {
26121             title: "Height",
26122             width: 40
26123         },   
26124         align: {
26125             title: "Align",
26126             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
26127             width: 80
26128         },
26129         valign: {
26130             title: "Valign",
26131             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
26132             width: 80
26133         },
26134         colspan: {
26135             title: "Colspan",
26136             width: 20
26137             
26138         }
26139     },
26140     'INPUT' : {
26141         name : {
26142             title: "name",
26143             width: 120
26144         },
26145         value : {
26146             title: "Value",
26147             width: 120
26148         },
26149         width : {
26150             title: "Width",
26151             width: 40
26152         }
26153     },
26154     'LABEL' : {
26155         'for' : {
26156             title: "For",
26157             width: 120
26158         }
26159     },
26160     'TEXTAREA' : {
26161           name : {
26162             title: "name",
26163             width: 120
26164         },
26165         rows : {
26166             title: "Rows",
26167             width: 20
26168         },
26169         cols : {
26170             title: "Cols",
26171             width: 20
26172         }
26173     },
26174     'SELECT' : {
26175         name : {
26176             title: "name",
26177             width: 120
26178         },
26179         selectoptions : {
26180             title: "Options",
26181             width: 200
26182         }
26183     },
26184     
26185     // should we really allow this??
26186     // should this just be 
26187     'BODY' : {
26188         title : {
26189             title: "title",
26190             width: 200,
26191             disabled : true
26192         }
26193     },
26194     '*' : {
26195         // empty..
26196     }
26197 };
26198
26199
26200
26201 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
26202     
26203     tb: false,
26204     
26205     rendered: false,
26206     
26207     editor : false,
26208     /**
26209      * @cfg {Object} disable  List of toolbar elements to disable
26210          
26211      */
26212     disable : false,
26213     /**
26214      * @cfg {Object} styles List of styles 
26215      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
26216      *
26217      * These must be defined in the page, so they get rendered correctly..
26218      * .headline { }
26219      * TD.underline { }
26220      * 
26221      */
26222     styles : false,
26223     
26224     
26225     
26226     toolbars : false,
26227     
26228     init : function(editor)
26229     {
26230         this.editor = editor;
26231         
26232         
26233         var fid = editor.frameId;
26234         var etb = this;
26235         function btn(id, toggle, handler){
26236             var xid = fid + '-'+ id ;
26237             return {
26238                 id : xid,
26239                 cmd : id,
26240                 cls : 'x-btn-icon x-edit-'+id,
26241                 enableToggle:toggle !== false,
26242                 scope: editor, // was editor...
26243                 handler:handler||editor.relayBtnCmd,
26244                 clickEvent:'mousedown',
26245                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
26246                 tabIndex:-1
26247             };
26248         }
26249         // create a new element.
26250         var wdiv = editor.wrap.createChild({
26251                 tag: 'div'
26252             }, editor.wrap.dom.firstChild.nextSibling, true);
26253         
26254         // can we do this more than once??
26255         
26256          // stop form submits
26257       
26258  
26259         // disable everything...
26260         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
26261         this.toolbars = {};
26262            
26263         for (var i in  ty) {
26264           
26265             this.toolbars[i] = this.buildToolbar(ty[i],i);
26266         }
26267         this.tb = this.toolbars.BODY;
26268         this.tb.el.show();
26269         this.buildFooter();
26270         this.footer.show();
26271          
26272         this.rendered = true;
26273         
26274         // the all the btns;
26275         editor.on('editorevent', this.updateToolbar, this);
26276         // other toolbars need to implement this..
26277         //editor.on('editmodechange', this.updateToolbar, this);
26278     },
26279     
26280     
26281     
26282     /**
26283      * Protected method that will not generally be called directly. It triggers
26284      * a toolbar update by reading the markup state of the current selection in the editor.
26285      */
26286     updateToolbar: function(ignore_a,ignore_b,sel){
26287
26288         
26289         if(!this.editor.activated){
26290              this.editor.onFirstFocus();
26291             return;
26292         }
26293         var updateFooter = sel ? false : true;
26294         
26295         
26296         var ans = this.editor.getAllAncestors();
26297         
26298         // pick
26299         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
26300         
26301         if (!sel) { 
26302             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
26303             sel = sel ? sel : this.editor.doc.body;
26304             sel = sel.tagName.length ? sel : this.editor.doc.body;
26305             
26306         }
26307         // pick a menu that exists..
26308         var tn = sel.tagName.toUpperCase();
26309         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
26310         
26311         tn = sel.tagName.toUpperCase();
26312         
26313         var lastSel = this.tb.selectedNode
26314         
26315         this.tb.selectedNode = sel;
26316         
26317         // if current menu does not match..
26318         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
26319                 
26320             this.tb.el.hide();
26321             ///console.log("show: " + tn);
26322             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
26323             this.tb.el.show();
26324             // update name
26325             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
26326             
26327             
26328             // update attributes
26329             if (this.tb.fields) {
26330                 this.tb.fields.each(function(e) {
26331                    e.setValue(sel.getAttribute(e.name));
26332                 });
26333             }
26334             
26335             // update styles
26336             var st = this.tb.fields.item(0);
26337             st.store.removeAll();
26338             var cn = sel.className.split(/\s+/);
26339             
26340             var avs = [];
26341             if (this.styles['*']) {
26342                 
26343                 Roo.each(this.styles['*'], function(v) {
26344                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
26345                 });
26346             }
26347             if (this.styles[tn]) { 
26348                 Roo.each(this.styles[tn], function(v) {
26349                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
26350                 });
26351             }
26352             
26353             st.store.loadData(avs);
26354             st.collapse();
26355             st.setValue(cn);
26356             
26357             // flag our selected Node.
26358             this.tb.selectedNode = sel;
26359            
26360            
26361             Roo.menu.MenuMgr.hideAll();
26362
26363         }
26364         
26365         if (!updateFooter) {
26366             return;
26367         }
26368         // update the footer
26369         //
26370         var html = '';
26371         
26372         this.footerEls = ans.reverse();
26373         Roo.each(this.footerEls, function(a,i) {
26374             if (!a) { return; }
26375             html += html.length ? ' &gt; '  :  '';
26376             
26377             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
26378             
26379         });
26380        
26381         // 
26382         var sz = this.footDisp.up('td').getSize();
26383         this.footDisp.dom.style.width = (sz.width -10) + 'px';
26384         this.footDisp.dom.style.marginLeft = '5px';
26385         
26386         this.footDisp.dom.style.overflow = 'hidden';
26387         
26388         this.footDisp.dom.innerHTML = html;
26389             
26390         //this.editorsyncValue();
26391     },
26392    
26393        
26394     // private
26395     onDestroy : function(){
26396         if(this.rendered){
26397             
26398             this.tb.items.each(function(item){
26399                 if(item.menu){
26400                     item.menu.removeAll();
26401                     if(item.menu.el){
26402                         item.menu.el.destroy();
26403                     }
26404                 }
26405                 item.destroy();
26406             });
26407              
26408         }
26409     },
26410     onFirstFocus: function() {
26411         // need to do this for all the toolbars..
26412         this.tb.items.each(function(item){
26413            item.enable();
26414         });
26415     },
26416     buildToolbar: function(tlist, nm)
26417     {
26418         var editor = this.editor;
26419          // create a new element.
26420         var wdiv = editor.wrap.createChild({
26421                 tag: 'div'
26422             }, editor.wrap.dom.firstChild.nextSibling, true);
26423         
26424        
26425         var tb = new Roo.Toolbar(wdiv);
26426         // add the name..
26427         
26428         tb.add(nm+ ":&nbsp;");
26429         
26430         // styles...
26431         if (this.styles) {
26432             
26433             // this needs a multi-select checkbox...
26434             tb.addField( new Roo.form.ComboBox({
26435                 store: new Roo.data.SimpleStore({
26436                     id : 'val',
26437                     fields: ['val', 'selected'],
26438                     data : [] 
26439                 }),
26440                 name : 'className',
26441                 displayField:'val',
26442                 typeAhead: false,
26443                 mode: 'local',
26444                 editable : false,
26445                 triggerAction: 'all',
26446                 emptyText:'Select Style',
26447                 selectOnFocus:true,
26448                 width: 130,
26449                 listeners : {
26450                     'select': function(c, r, i) {
26451                         // initial support only for on class per el..
26452                         tb.selectedNode.className =  r ? r.get('val') : '';
26453                     }
26454                 }
26455     
26456             }));
26457         }
26458             
26459         
26460         
26461         for (var i in tlist) {
26462             
26463             var item = tlist[i];
26464             tb.add(item.title + ":&nbsp;");
26465             
26466             
26467             
26468             
26469             if (item.opts) {
26470                 // opts == pulldown..
26471                 tb.addField( new Roo.form.ComboBox({
26472                     store: new Roo.data.SimpleStore({
26473                         id : 'val',
26474                         fields: ['val'],
26475                         data : item.opts  
26476                     }),
26477                     name : i,
26478                     displayField:'val',
26479                     typeAhead: false,
26480                     mode: 'local',
26481                     editable : false,
26482                     triggerAction: 'all',
26483                     emptyText:'Select',
26484                     selectOnFocus:true,
26485                     width: item.width ? item.width  : 130,
26486                     listeners : {
26487                         'select': function(c, r, i) {
26488                             tb.selectedNode.setAttribute(c.name, r.get('val'));
26489                         }
26490                     }
26491
26492                 }));
26493                 continue;
26494                     
26495                  
26496                 
26497                 tb.addField( new Roo.form.TextField({
26498                     name: i,
26499                     width: 100,
26500                     //allowBlank:false,
26501                     value: ''
26502                 }));
26503                 continue;
26504             }
26505             tb.addField( new Roo.form.TextField({
26506                 name: i,
26507                 width: item.width,
26508                 //allowBlank:true,
26509                 value: '',
26510                 listeners: {
26511                     'change' : function(f, nv, ov) {
26512                         tb.selectedNode.setAttribute(f.name, nv);
26513                     }
26514                 }
26515             }));
26516              
26517         }
26518         tb.el.on('click', function(e){
26519             e.preventDefault(); // what does this do?
26520         });
26521         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
26522         tb.el.hide();
26523         tb.name = nm;
26524         // dont need to disable them... as they will get hidden
26525         return tb;
26526          
26527         
26528     },
26529     buildFooter : function()
26530     {
26531         
26532         var fel = this.editor.wrap.createChild();
26533         this.footer = new Roo.Toolbar(fel);
26534         // toolbar has scrolly on left / right?
26535         var footDisp= new Roo.Toolbar.Fill();
26536         var _t = this;
26537         this.footer.add(
26538             {
26539                 text : '&lt;',
26540                 xtype: 'Button',
26541                 handler : function() {
26542                     _t.footDisp.scrollTo('left',0,true)
26543                 }
26544             }
26545         );
26546         this.footer.add( footDisp );
26547         this.footer.add( 
26548             {
26549                 text : '&gt;',
26550                 xtype: 'Button',
26551                 handler : function() {
26552                     // no animation..
26553                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
26554                 }
26555             }
26556         );
26557         var fel = Roo.get(footDisp.el);
26558         fel.addClass('x-editor-context');
26559         this.footDispWrap = fel; 
26560         this.footDispWrap.overflow  = 'hidden';
26561         
26562         this.footDisp = fel.createChild();
26563         this.footDispWrap.on('click', this.onContextClick, this)
26564         
26565         
26566     },
26567     onContextClick : function (ev,dom)
26568     {
26569         ev.preventDefault();
26570         var  cn = dom.className;
26571         Roo.log(cn);
26572         if (!cn.match(/x-ed-loc-/)) {
26573             return;
26574         }
26575         var n = cn.split('-').pop();
26576         var ans = this.footerEls;
26577         var sel = ans[n];
26578         
26579          // pick
26580         var range = this.editor.createRange();
26581         
26582         range.selectNodeContents(sel);
26583         //range.selectNode(sel);
26584         
26585         
26586         var selection = this.editor.getSelection();
26587         selection.removeAllRanges();
26588         selection.addRange(range);
26589         
26590         
26591         
26592         this.updateToolbar(null, null, sel);
26593         
26594         
26595     }
26596     
26597     
26598     
26599     
26600     
26601 });
26602
26603
26604
26605
26606
26607 /*
26608  * Based on:
26609  * Ext JS Library 1.1.1
26610  * Copyright(c) 2006-2007, Ext JS, LLC.
26611  *
26612  * Originally Released Under LGPL - original licence link has changed is not relivant.
26613  *
26614  * Fork - LGPL
26615  * <script type="text/javascript">
26616  */
26617  
26618 /**
26619  * @class Roo.form.BasicForm
26620  * @extends Roo.util.Observable
26621  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
26622  * @constructor
26623  * @param {String/HTMLElement/Roo.Element} el The form element or its id
26624  * @param {Object} config Configuration options
26625  */
26626 Roo.form.BasicForm = function(el, config){
26627     this.allItems = [];
26628     this.childForms = [];
26629     Roo.apply(this, config);
26630     /*
26631      * The Roo.form.Field items in this form.
26632      * @type MixedCollection
26633      */
26634      
26635      
26636     this.items = new Roo.util.MixedCollection(false, function(o){
26637         return o.id || (o.id = Roo.id());
26638     });
26639     this.addEvents({
26640         /**
26641          * @event beforeaction
26642          * Fires before any action is performed. Return false to cancel the action.
26643          * @param {Form} this
26644          * @param {Action} action The action to be performed
26645          */
26646         beforeaction: true,
26647         /**
26648          * @event actionfailed
26649          * Fires when an action fails.
26650          * @param {Form} this
26651          * @param {Action} action The action that failed
26652          */
26653         actionfailed : true,
26654         /**
26655          * @event actioncomplete
26656          * Fires when an action is completed.
26657          * @param {Form} this
26658          * @param {Action} action The action that completed
26659          */
26660         actioncomplete : true
26661     });
26662     if(el){
26663         this.initEl(el);
26664     }
26665     Roo.form.BasicForm.superclass.constructor.call(this);
26666 };
26667
26668 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
26669     /**
26670      * @cfg {String} method
26671      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
26672      */
26673     /**
26674      * @cfg {DataReader} reader
26675      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
26676      * This is optional as there is built-in support for processing JSON.
26677      */
26678     /**
26679      * @cfg {DataReader} errorReader
26680      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
26681      * This is completely optional as there is built-in support for processing JSON.
26682      */
26683     /**
26684      * @cfg {String} url
26685      * The URL to use for form actions if one isn't supplied in the action options.
26686      */
26687     /**
26688      * @cfg {Boolean} fileUpload
26689      * Set to true if this form is a file upload.
26690      */
26691      
26692     /**
26693      * @cfg {Object} baseParams
26694      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
26695      */
26696      /**
26697      
26698     /**
26699      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
26700      */
26701     timeout: 30,
26702
26703     // private
26704     activeAction : null,
26705
26706     /**
26707      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
26708      * or setValues() data instead of when the form was first created.
26709      */
26710     trackResetOnLoad : false,
26711     
26712     
26713     /**
26714      * childForms - used for multi-tab forms
26715      * @type {Array}
26716      */
26717     childForms : false,
26718     
26719     /**
26720      * allItems - full list of fields.
26721      * @type {Array}
26722      */
26723     allItems : false,
26724     
26725     /**
26726      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
26727      * element by passing it or its id or mask the form itself by passing in true.
26728      * @type Mixed
26729      */
26730     waitMsgTarget : false,
26731
26732     // private
26733     initEl : function(el){
26734         this.el = Roo.get(el);
26735         this.id = this.el.id || Roo.id();
26736         this.el.on('submit', this.onSubmit, this);
26737         this.el.addClass('x-form');
26738     },
26739
26740     // private
26741     onSubmit : function(e){
26742         e.stopEvent();
26743     },
26744
26745     /**
26746      * Returns true if client-side validation on the form is successful.
26747      * @return Boolean
26748      */
26749     isValid : function(){
26750         var valid = true;
26751         this.items.each(function(f){
26752            if(!f.validate()){
26753                valid = false;
26754            }
26755         });
26756         return valid;
26757     },
26758
26759     /**
26760      * Returns true if any fields in this form have changed since their original load.
26761      * @return Boolean
26762      */
26763     isDirty : function(){
26764         var dirty = false;
26765         this.items.each(function(f){
26766            if(f.isDirty()){
26767                dirty = true;
26768                return false;
26769            }
26770         });
26771         return dirty;
26772     },
26773
26774     /**
26775      * Performs a predefined action (submit or load) or custom actions you define on this form.
26776      * @param {String} actionName The name of the action type
26777      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
26778      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
26779      * accept other config options):
26780      * <pre>
26781 Property          Type             Description
26782 ----------------  ---------------  ----------------------------------------------------------------------------------
26783 url               String           The url for the action (defaults to the form's url)
26784 method            String           The form method to use (defaults to the form's method, or POST if not defined)
26785 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
26786 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
26787                                    validate the form on the client (defaults to false)
26788      * </pre>
26789      * @return {BasicForm} this
26790      */
26791     doAction : function(action, options){
26792         if(typeof action == 'string'){
26793             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
26794         }
26795         if(this.fireEvent('beforeaction', this, action) !== false){
26796             this.beforeAction(action);
26797             action.run.defer(100, action);
26798         }
26799         return this;
26800     },
26801
26802     /**
26803      * Shortcut to do a submit action.
26804      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26805      * @return {BasicForm} this
26806      */
26807     submit : function(options){
26808         this.doAction('submit', options);
26809         return this;
26810     },
26811
26812     /**
26813      * Shortcut to do a load action.
26814      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26815      * @return {BasicForm} this
26816      */
26817     load : function(options){
26818         this.doAction('load', options);
26819         return this;
26820     },
26821
26822     /**
26823      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
26824      * @param {Record} record The record to edit
26825      * @return {BasicForm} this
26826      */
26827     updateRecord : function(record){
26828         record.beginEdit();
26829         var fs = record.fields;
26830         fs.each(function(f){
26831             var field = this.findField(f.name);
26832             if(field){
26833                 record.set(f.name, field.getValue());
26834             }
26835         }, this);
26836         record.endEdit();
26837         return this;
26838     },
26839
26840     /**
26841      * Loads an Roo.data.Record into this form.
26842      * @param {Record} record The record to load
26843      * @return {BasicForm} this
26844      */
26845     loadRecord : function(record){
26846         this.setValues(record.data);
26847         return this;
26848     },
26849
26850     // private
26851     beforeAction : function(action){
26852         var o = action.options;
26853         
26854        
26855         if(this.waitMsgTarget === true){
26856             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
26857         }else if(this.waitMsgTarget){
26858             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
26859             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
26860         }else {
26861             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
26862         }
26863          
26864     },
26865
26866     // private
26867     afterAction : function(action, success){
26868         this.activeAction = null;
26869         var o = action.options;
26870         
26871         if(this.waitMsgTarget === true){
26872             this.el.unmask();
26873         }else if(this.waitMsgTarget){
26874             this.waitMsgTarget.unmask();
26875         }else{
26876             Roo.MessageBox.updateProgress(1);
26877             Roo.MessageBox.hide();
26878         }
26879          
26880         if(success){
26881             if(o.reset){
26882                 this.reset();
26883             }
26884             Roo.callback(o.success, o.scope, [this, action]);
26885             this.fireEvent('actioncomplete', this, action);
26886             
26887         }else{
26888             Roo.callback(o.failure, o.scope, [this, action]);
26889             // show an error message if no failed handler is set..
26890             if (!this.hasListener('actionfailed')) {
26891                 Roo.MessageBox.alert("Error",
26892                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
26893                         action.result.errorMsg :
26894                         "Saving Failed, please check your entries"
26895                 );
26896             }
26897             
26898             this.fireEvent('actionfailed', this, action);
26899         }
26900         
26901     },
26902
26903     /**
26904      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
26905      * @param {String} id The value to search for
26906      * @return Field
26907      */
26908     findField : function(id){
26909         var field = this.items.get(id);
26910         if(!field){
26911             this.items.each(function(f){
26912                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
26913                     field = f;
26914                     return false;
26915                 }
26916             });
26917         }
26918         return field || null;
26919     },
26920
26921     /**
26922      * Add a secondary form to this one, 
26923      * Used to provide tabbed forms. One form is primary, with hidden values 
26924      * which mirror the elements from the other forms.
26925      * 
26926      * @param {Roo.form.Form} form to add.
26927      * 
26928      */
26929     addForm : function(form)
26930     {
26931        
26932         if (this.childForms.indexOf(form) > -1) {
26933             // already added..
26934             return;
26935         }
26936         this.childForms.push(form);
26937         var n = '';
26938         Roo.each(form.allItems, function (fe) {
26939             
26940             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
26941             if (this.findField(n)) { // already added..
26942                 return;
26943             }
26944             var add = new Roo.form.Hidden({
26945                 name : n
26946             });
26947             add.render(this.el);
26948             
26949             this.add( add );
26950         }, this);
26951         
26952     },
26953     /**
26954      * Mark fields in this form invalid in bulk.
26955      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
26956      * @return {BasicForm} this
26957      */
26958     markInvalid : function(errors){
26959         if(errors instanceof Array){
26960             for(var i = 0, len = errors.length; i < len; i++){
26961                 var fieldError = errors[i];
26962                 var f = this.findField(fieldError.id);
26963                 if(f){
26964                     f.markInvalid(fieldError.msg);
26965                 }
26966             }
26967         }else{
26968             var field, id;
26969             for(id in errors){
26970                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
26971                     field.markInvalid(errors[id]);
26972                 }
26973             }
26974         }
26975         Roo.each(this.childForms || [], function (f) {
26976             f.markInvalid(errors);
26977         });
26978         
26979         return this;
26980     },
26981
26982     /**
26983      * Set values for fields in this form in bulk.
26984      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
26985      * @return {BasicForm} this
26986      */
26987     setValues : function(values){
26988         if(values instanceof Array){ // array of objects
26989             for(var i = 0, len = values.length; i < len; i++){
26990                 var v = values[i];
26991                 var f = this.findField(v.id);
26992                 if(f){
26993                     f.setValue(v.value);
26994                     if(this.trackResetOnLoad){
26995                         f.originalValue = f.getValue();
26996                     }
26997                 }
26998             }
26999         }else{ // object hash
27000             var field, id;
27001             for(id in values){
27002                 if(typeof values[id] != 'function' && (field = this.findField(id))){
27003                     
27004                     if (field.setFromData && 
27005                         field.valueField && 
27006                         field.displayField &&
27007                         // combos' with local stores can 
27008                         // be queried via setValue()
27009                         // to set their value..
27010                         (field.store && !field.store.isLocal)
27011                         ) {
27012                         // it's a combo
27013                         var sd = { };
27014                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
27015                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
27016                         field.setFromData(sd);
27017                         
27018                     } else {
27019                         field.setValue(values[id]);
27020                     }
27021                     
27022                     
27023                     if(this.trackResetOnLoad){
27024                         field.originalValue = field.getValue();
27025                     }
27026                 }
27027             }
27028         }
27029          
27030         Roo.each(this.childForms || [], function (f) {
27031             f.setValues(values);
27032         });
27033                 
27034         return this;
27035     },
27036
27037     /**
27038      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
27039      * they are returned as an array.
27040      * @param {Boolean} asString
27041      * @return {Object}
27042      */
27043     getValues : function(asString){
27044         if (this.childForms) {
27045             // copy values from the child forms
27046             Roo.each(this.childForms, function (f) {
27047                 this.setValues(f.getValues());
27048             }, this);
27049         }
27050         
27051         
27052         
27053         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
27054         if(asString === true){
27055             return fs;
27056         }
27057         return Roo.urlDecode(fs);
27058     },
27059     
27060     /**
27061      * Returns the fields in this form as an object with key/value pairs. 
27062      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
27063      * @return {Object}
27064      */
27065     getFieldValues : function(with_hidden)
27066     {
27067         if (this.childForms) {
27068             // copy values from the child forms
27069             // should this call getFieldValues - probably not as we do not currently copy
27070             // hidden fields when we generate..
27071             Roo.each(this.childForms, function (f) {
27072                 this.setValues(f.getValues());
27073             }, this);
27074         }
27075         
27076         var ret = {};
27077         this.items.each(function(f){
27078             if (!f.getName()) {
27079                 return;
27080             }
27081             var v = f.getValue();
27082             // not sure if this supported any more..
27083             if ((typeof(v) == 'object') && f.getRawValue) {
27084                 v = f.getRawValue() ; // dates..
27085             }
27086             // combo boxes where name != hiddenName...
27087             if (f.name != f.getName()) {
27088                 ret[f.name] = f.getRawValue();
27089             }
27090             ret[f.getName()] = v;
27091         });
27092         
27093         return ret;
27094     },
27095
27096     /**
27097      * Clears all invalid messages in this form.
27098      * @return {BasicForm} this
27099      */
27100     clearInvalid : function(){
27101         this.items.each(function(f){
27102            f.clearInvalid();
27103         });
27104         
27105         Roo.each(this.childForms || [], function (f) {
27106             f.clearInvalid();
27107         });
27108         
27109         
27110         return this;
27111     },
27112
27113     /**
27114      * Resets this form.
27115      * @return {BasicForm} this
27116      */
27117     reset : function(){
27118         this.items.each(function(f){
27119             f.reset();
27120         });
27121         
27122         Roo.each(this.childForms || [], function (f) {
27123             f.reset();
27124         });
27125        
27126         
27127         return this;
27128     },
27129
27130     /**
27131      * Add Roo.form components to this form.
27132      * @param {Field} field1
27133      * @param {Field} field2 (optional)
27134      * @param {Field} etc (optional)
27135      * @return {BasicForm} this
27136      */
27137     add : function(){
27138         this.items.addAll(Array.prototype.slice.call(arguments, 0));
27139         return this;
27140     },
27141
27142
27143     /**
27144      * Removes a field from the items collection (does NOT remove its markup).
27145      * @param {Field} field
27146      * @return {BasicForm} this
27147      */
27148     remove : function(field){
27149         this.items.remove(field);
27150         return this;
27151     },
27152
27153     /**
27154      * Looks at the fields in this form, checks them for an id attribute,
27155      * and calls applyTo on the existing dom element with that id.
27156      * @return {BasicForm} this
27157      */
27158     render : function(){
27159         this.items.each(function(f){
27160             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
27161                 f.applyTo(f.id);
27162             }
27163         });
27164         return this;
27165     },
27166
27167     /**
27168      * Calls {@link Ext#apply} for all fields in this form with the passed object.
27169      * @param {Object} values
27170      * @return {BasicForm} this
27171      */
27172     applyToFields : function(o){
27173         this.items.each(function(f){
27174            Roo.apply(f, o);
27175         });
27176         return this;
27177     },
27178
27179     /**
27180      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
27181      * @param {Object} values
27182      * @return {BasicForm} this
27183      */
27184     applyIfToFields : function(o){
27185         this.items.each(function(f){
27186            Roo.applyIf(f, o);
27187         });
27188         return this;
27189     }
27190 });
27191
27192 // back compat
27193 Roo.BasicForm = Roo.form.BasicForm;/*
27194  * Based on:
27195  * Ext JS Library 1.1.1
27196  * Copyright(c) 2006-2007, Ext JS, LLC.
27197  *
27198  * Originally Released Under LGPL - original licence link has changed is not relivant.
27199  *
27200  * Fork - LGPL
27201  * <script type="text/javascript">
27202  */
27203
27204 /**
27205  * @class Roo.form.Form
27206  * @extends Roo.form.BasicForm
27207  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
27208  * @constructor
27209  * @param {Object} config Configuration options
27210  */
27211 Roo.form.Form = function(config){
27212     var xitems =  [];
27213     if (config.items) {
27214         xitems = config.items;
27215         delete config.items;
27216     }
27217    
27218     
27219     Roo.form.Form.superclass.constructor.call(this, null, config);
27220     this.url = this.url || this.action;
27221     if(!this.root){
27222         this.root = new Roo.form.Layout(Roo.applyIf({
27223             id: Roo.id()
27224         }, config));
27225     }
27226     this.active = this.root;
27227     /**
27228      * Array of all the buttons that have been added to this form via {@link addButton}
27229      * @type Array
27230      */
27231     this.buttons = [];
27232     this.allItems = [];
27233     this.addEvents({
27234         /**
27235          * @event clientvalidation
27236          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
27237          * @param {Form} this
27238          * @param {Boolean} valid true if the form has passed client-side validation
27239          */
27240         clientvalidation: true,
27241         /**
27242          * @event rendered
27243          * Fires when the form is rendered
27244          * @param {Roo.form.Form} form
27245          */
27246         rendered : true
27247     });
27248     
27249     if (this.progressUrl) {
27250             // push a hidden field onto the list of fields..
27251             this.addxtype( {
27252                     xns: Roo.form, 
27253                     xtype : 'Hidden', 
27254                     name : 'UPLOAD_IDENTIFIER' 
27255             });
27256         }
27257         
27258     
27259     Roo.each(xitems, this.addxtype, this);
27260     
27261     
27262     
27263 };
27264
27265 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
27266     /**
27267      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
27268      */
27269     /**
27270      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
27271      */
27272     /**
27273      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
27274      */
27275     buttonAlign:'center',
27276
27277     /**
27278      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
27279      */
27280     minButtonWidth:75,
27281
27282     /**
27283      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
27284      * This property cascades to child containers if not set.
27285      */
27286     labelAlign:'left',
27287
27288     /**
27289      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
27290      * fires a looping event with that state. This is required to bind buttons to the valid
27291      * state using the config value formBind:true on the button.
27292      */
27293     monitorValid : false,
27294
27295     /**
27296      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
27297      */
27298     monitorPoll : 200,
27299     
27300     /**
27301      * @cfg {String} progressUrl - Url to return progress data 
27302      */
27303     
27304     progressUrl : false,
27305   
27306     /**
27307      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
27308      * fields are added and the column is closed. If no fields are passed the column remains open
27309      * until end() is called.
27310      * @param {Object} config The config to pass to the column
27311      * @param {Field} field1 (optional)
27312      * @param {Field} field2 (optional)
27313      * @param {Field} etc (optional)
27314      * @return Column The column container object
27315      */
27316     column : function(c){
27317         var col = new Roo.form.Column(c);
27318         this.start(col);
27319         if(arguments.length > 1){ // duplicate code required because of Opera
27320             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
27321             this.end();
27322         }
27323         return col;
27324     },
27325
27326     /**
27327      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
27328      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
27329      * until end() is called.
27330      * @param {Object} config The config to pass to the fieldset
27331      * @param {Field} field1 (optional)
27332      * @param {Field} field2 (optional)
27333      * @param {Field} etc (optional)
27334      * @return FieldSet The fieldset container object
27335      */
27336     fieldset : function(c){
27337         var fs = new Roo.form.FieldSet(c);
27338         this.start(fs);
27339         if(arguments.length > 1){ // duplicate code required because of Opera
27340             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
27341             this.end();
27342         }
27343         return fs;
27344     },
27345
27346     /**
27347      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
27348      * fields are added and the container is closed. If no fields are passed the container remains open
27349      * until end() is called.
27350      * @param {Object} config The config to pass to the Layout
27351      * @param {Field} field1 (optional)
27352      * @param {Field} field2 (optional)
27353      * @param {Field} etc (optional)
27354      * @return Layout The container object
27355      */
27356     container : function(c){
27357         var l = new Roo.form.Layout(c);
27358         this.start(l);
27359         if(arguments.length > 1){ // duplicate code required because of Opera
27360             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
27361             this.end();
27362         }
27363         return l;
27364     },
27365
27366     /**
27367      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
27368      * @param {Object} container A Roo.form.Layout or subclass of Layout
27369      * @return {Form} this
27370      */
27371     start : function(c){
27372         // cascade label info
27373         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
27374         this.active.stack.push(c);
27375         c.ownerCt = this.active;
27376         this.active = c;
27377         return this;
27378     },
27379
27380     /**
27381      * Closes the current open container
27382      * @return {Form} this
27383      */
27384     end : function(){
27385         if(this.active == this.root){
27386             return this;
27387         }
27388         this.active = this.active.ownerCt;
27389         return this;
27390     },
27391
27392     /**
27393      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
27394      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
27395      * as the label of the field.
27396      * @param {Field} field1
27397      * @param {Field} field2 (optional)
27398      * @param {Field} etc. (optional)
27399      * @return {Form} this
27400      */
27401     add : function(){
27402         this.active.stack.push.apply(this.active.stack, arguments);
27403         this.allItems.push.apply(this.allItems,arguments);
27404         var r = [];
27405         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
27406             if(a[i].isFormField){
27407                 r.push(a[i]);
27408             }
27409         }
27410         if(r.length > 0){
27411             Roo.form.Form.superclass.add.apply(this, r);
27412         }
27413         return this;
27414     },
27415     
27416
27417     
27418     
27419     
27420      /**
27421      * Find any element that has been added to a form, using it's ID or name
27422      * This can include framesets, columns etc. along with regular fields..
27423      * @param {String} id - id or name to find.
27424      
27425      * @return {Element} e - or false if nothing found.
27426      */
27427     findbyId : function(id)
27428     {
27429         var ret = false;
27430         if (!id) {
27431             return ret;
27432         }
27433         Roo.each(this.allItems, function(f){
27434             if (f.id == id || f.name == id ){
27435                 ret = f;
27436                 return false;
27437             }
27438         });
27439         return ret;
27440     },
27441
27442     
27443     
27444     /**
27445      * Render this form into the passed container. This should only be called once!
27446      * @param {String/HTMLElement/Element} container The element this component should be rendered into
27447      * @return {Form} this
27448      */
27449     render : function(ct)
27450     {
27451         
27452         
27453         
27454         ct = Roo.get(ct);
27455         var o = this.autoCreate || {
27456             tag: 'form',
27457             method : this.method || 'POST',
27458             id : this.id || Roo.id()
27459         };
27460         this.initEl(ct.createChild(o));
27461
27462         this.root.render(this.el);
27463         
27464        
27465              
27466         this.items.each(function(f){
27467             f.render('x-form-el-'+f.id);
27468         });
27469
27470         if(this.buttons.length > 0){
27471             // tables are required to maintain order and for correct IE layout
27472             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
27473                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
27474                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
27475             }}, null, true);
27476             var tr = tb.getElementsByTagName('tr')[0];
27477             for(var i = 0, len = this.buttons.length; i < len; i++) {
27478                 var b = this.buttons[i];
27479                 var td = document.createElement('td');
27480                 td.className = 'x-form-btn-td';
27481                 b.render(tr.appendChild(td));
27482             }
27483         }
27484         if(this.monitorValid){ // initialize after render
27485             this.startMonitoring();
27486         }
27487         this.fireEvent('rendered', this);
27488         return this;
27489     },
27490
27491     /**
27492      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
27493      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
27494      * object or a valid Roo.DomHelper element config
27495      * @param {Function} handler The function called when the button is clicked
27496      * @param {Object} scope (optional) The scope of the handler function
27497      * @return {Roo.Button}
27498      */
27499     addButton : function(config, handler, scope){
27500         var bc = {
27501             handler: handler,
27502             scope: scope,
27503             minWidth: this.minButtonWidth,
27504             hideParent:true
27505         };
27506         if(typeof config == "string"){
27507             bc.text = config;
27508         }else{
27509             Roo.apply(bc, config);
27510         }
27511         var btn = new Roo.Button(null, bc);
27512         this.buttons.push(btn);
27513         return btn;
27514     },
27515
27516      /**
27517      * Adds a series of form elements (using the xtype property as the factory method.
27518      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
27519      * @param {Object} config 
27520      */
27521     
27522     addxtype : function()
27523     {
27524         var ar = Array.prototype.slice.call(arguments, 0);
27525         var ret = false;
27526         for(var i = 0; i < ar.length; i++) {
27527             if (!ar[i]) {
27528                 continue; // skip -- if this happends something invalid got sent, we 
27529                 // should ignore it, as basically that interface element will not show up
27530                 // and that should be pretty obvious!!
27531             }
27532             
27533             if (Roo.form[ar[i].xtype]) {
27534                 ar[i].form = this;
27535                 var fe = Roo.factory(ar[i], Roo.form);
27536                 if (!ret) {
27537                     ret = fe;
27538                 }
27539                 fe.form = this;
27540                 if (fe.store) {
27541                     fe.store.form = this;
27542                 }
27543                 if (fe.isLayout) {  
27544                          
27545                     this.start(fe);
27546                     this.allItems.push(fe);
27547                     if (fe.items && fe.addxtype) {
27548                         fe.addxtype.apply(fe, fe.items);
27549                         delete fe.items;
27550                     }
27551                      this.end();
27552                     continue;
27553                 }
27554                 
27555                 
27556                  
27557                 this.add(fe);
27558               //  console.log('adding ' + ar[i].xtype);
27559             }
27560             if (ar[i].xtype == 'Button') {  
27561                 //console.log('adding button');
27562                 //console.log(ar[i]);
27563                 this.addButton(ar[i]);
27564                 this.allItems.push(fe);
27565                 continue;
27566             }
27567             
27568             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
27569                 alert('end is not supported on xtype any more, use items');
27570             //    this.end();
27571             //    //console.log('adding end');
27572             }
27573             
27574         }
27575         return ret;
27576     },
27577     
27578     /**
27579      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
27580      * option "monitorValid"
27581      */
27582     startMonitoring : function(){
27583         if(!this.bound){
27584             this.bound = true;
27585             Roo.TaskMgr.start({
27586                 run : this.bindHandler,
27587                 interval : this.monitorPoll || 200,
27588                 scope: this
27589             });
27590         }
27591     },
27592
27593     /**
27594      * Stops monitoring of the valid state of this form
27595      */
27596     stopMonitoring : function(){
27597         this.bound = false;
27598     },
27599
27600     // private
27601     bindHandler : function(){
27602         if(!this.bound){
27603             return false; // stops binding
27604         }
27605         var valid = true;
27606         this.items.each(function(f){
27607             if(!f.isValid(true)){
27608                 valid = false;
27609                 return false;
27610             }
27611         });
27612         for(var i = 0, len = this.buttons.length; i < len; i++){
27613             var btn = this.buttons[i];
27614             if(btn.formBind === true && btn.disabled === valid){
27615                 btn.setDisabled(!valid);
27616             }
27617         }
27618         this.fireEvent('clientvalidation', this, valid);
27619     }
27620     
27621     
27622     
27623     
27624     
27625     
27626     
27627     
27628 });
27629
27630
27631 // back compat
27632 Roo.Form = Roo.form.Form;
27633 /*
27634  * Based on:
27635  * Ext JS Library 1.1.1
27636  * Copyright(c) 2006-2007, Ext JS, LLC.
27637  *
27638  * Originally Released Under LGPL - original licence link has changed is not relivant.
27639  *
27640  * Fork - LGPL
27641  * <script type="text/javascript">
27642  */
27643  
27644  /**
27645  * @class Roo.form.Action
27646  * Internal Class used to handle form actions
27647  * @constructor
27648  * @param {Roo.form.BasicForm} el The form element or its id
27649  * @param {Object} config Configuration options
27650  */
27651  
27652  
27653 // define the action interface
27654 Roo.form.Action = function(form, options){
27655     this.form = form;
27656     this.options = options || {};
27657 };
27658 /**
27659  * Client Validation Failed
27660  * @const 
27661  */
27662 Roo.form.Action.CLIENT_INVALID = 'client';
27663 /**
27664  * Server Validation Failed
27665  * @const 
27666  */
27667  Roo.form.Action.SERVER_INVALID = 'server';
27668  /**
27669  * Connect to Server Failed
27670  * @const 
27671  */
27672 Roo.form.Action.CONNECT_FAILURE = 'connect';
27673 /**
27674  * Reading Data from Server Failed
27675  * @const 
27676  */
27677 Roo.form.Action.LOAD_FAILURE = 'load';
27678
27679 Roo.form.Action.prototype = {
27680     type : 'default',
27681     failureType : undefined,
27682     response : undefined,
27683     result : undefined,
27684
27685     // interface method
27686     run : function(options){
27687
27688     },
27689
27690     // interface method
27691     success : function(response){
27692
27693     },
27694
27695     // interface method
27696     handleResponse : function(response){
27697
27698     },
27699
27700     // default connection failure
27701     failure : function(response){
27702         
27703         this.response = response;
27704         this.failureType = Roo.form.Action.CONNECT_FAILURE;
27705         this.form.afterAction(this, false);
27706     },
27707
27708     processResponse : function(response){
27709         this.response = response;
27710         if(!response.responseText){
27711             return true;
27712         }
27713         this.result = this.handleResponse(response);
27714         return this.result;
27715     },
27716
27717     // utility functions used internally
27718     getUrl : function(appendParams){
27719         var url = this.options.url || this.form.url || this.form.el.dom.action;
27720         if(appendParams){
27721             var p = this.getParams();
27722             if(p){
27723                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
27724             }
27725         }
27726         return url;
27727     },
27728
27729     getMethod : function(){
27730         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
27731     },
27732
27733     getParams : function(){
27734         var bp = this.form.baseParams;
27735         var p = this.options.params;
27736         if(p){
27737             if(typeof p == "object"){
27738                 p = Roo.urlEncode(Roo.applyIf(p, bp));
27739             }else if(typeof p == 'string' && bp){
27740                 p += '&' + Roo.urlEncode(bp);
27741             }
27742         }else if(bp){
27743             p = Roo.urlEncode(bp);
27744         }
27745         return p;
27746     },
27747
27748     createCallback : function(){
27749         return {
27750             success: this.success,
27751             failure: this.failure,
27752             scope: this,
27753             timeout: (this.form.timeout*1000),
27754             upload: this.form.fileUpload ? this.success : undefined
27755         };
27756     }
27757 };
27758
27759 Roo.form.Action.Submit = function(form, options){
27760     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
27761 };
27762
27763 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
27764     type : 'submit',
27765
27766     haveProgress : false,
27767     uploadComplete : false,
27768     
27769     // uploadProgress indicator.
27770     uploadProgress : function()
27771     {
27772         if (!this.form.progressUrl) {
27773             return;
27774         }
27775         
27776         if (!this.haveProgress) {
27777             Roo.MessageBox.progress("Uploading", "Uploading");
27778         }
27779         if (this.uploadComplete) {
27780            Roo.MessageBox.hide();
27781            return;
27782         }
27783         
27784         this.haveProgress = true;
27785    
27786         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
27787         
27788         var c = new Roo.data.Connection();
27789         c.request({
27790             url : this.form.progressUrl,
27791             params: {
27792                 id : uid
27793             },
27794             method: 'GET',
27795             success : function(req){
27796                //console.log(data);
27797                 var rdata = false;
27798                 var edata;
27799                 try  {
27800                    rdata = Roo.decode(req.responseText)
27801                 } catch (e) {
27802                     Roo.log("Invalid data from server..");
27803                     Roo.log(edata);
27804                     return;
27805                 }
27806                 if (!rdata || !rdata.success) {
27807                     Roo.log(rdata);
27808                     return;
27809                 }
27810                 var data = rdata.data;
27811                 
27812                 if (this.uploadComplete) {
27813                    Roo.MessageBox.hide();
27814                    return;
27815                 }
27816                    
27817                 if (data){
27818                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
27819                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
27820                     );
27821                 }
27822                 this.uploadProgress.defer(2000,this);
27823             },
27824        
27825             failure: function(data) {
27826                 Roo.log('progress url failed ');
27827                 Roo.log(data);
27828             },
27829             scope : this
27830         });
27831            
27832     },
27833     
27834     
27835     run : function()
27836     {
27837         // run get Values on the form, so it syncs any secondary forms.
27838         this.form.getValues();
27839         
27840         var o = this.options;
27841         var method = this.getMethod();
27842         var isPost = method == 'POST';
27843         if(o.clientValidation === false || this.form.isValid()){
27844             
27845             if (this.form.progressUrl) {
27846                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
27847                     (new Date() * 1) + '' + Math.random());
27848                     
27849             } 
27850             
27851             
27852             Roo.Ajax.request(Roo.apply(this.createCallback(), {
27853                 form:this.form.el.dom,
27854                 url:this.getUrl(!isPost),
27855                 method: method,
27856                 params:isPost ? this.getParams() : null,
27857                 isUpload: this.form.fileUpload
27858             }));
27859             
27860             this.uploadProgress();
27861
27862         }else if (o.clientValidation !== false){ // client validation failed
27863             this.failureType = Roo.form.Action.CLIENT_INVALID;
27864             this.form.afterAction(this, false);
27865         }
27866     },
27867
27868     success : function(response)
27869     {
27870         this.uploadComplete= true;
27871         if (this.haveProgress) {
27872             Roo.MessageBox.hide();
27873         }
27874         
27875         
27876         var result = this.processResponse(response);
27877         if(result === true || result.success){
27878             this.form.afterAction(this, true);
27879             return;
27880         }
27881         if(result.errors){
27882             this.form.markInvalid(result.errors);
27883             this.failureType = Roo.form.Action.SERVER_INVALID;
27884         }
27885         this.form.afterAction(this, false);
27886     },
27887     failure : function(response)
27888     {
27889         this.uploadComplete= true;
27890         if (this.haveProgress) {
27891             Roo.MessageBox.hide();
27892         }
27893         
27894         this.response = response;
27895         this.failureType = Roo.form.Action.CONNECT_FAILURE;
27896         this.form.afterAction(this, false);
27897     },
27898     
27899     handleResponse : function(response){
27900         if(this.form.errorReader){
27901             var rs = this.form.errorReader.read(response);
27902             var errors = [];
27903             if(rs.records){
27904                 for(var i = 0, len = rs.records.length; i < len; i++) {
27905                     var r = rs.records[i];
27906                     errors[i] = r.data;
27907                 }
27908             }
27909             if(errors.length < 1){
27910                 errors = null;
27911             }
27912             return {
27913                 success : rs.success,
27914                 errors : errors
27915             };
27916         }
27917         var ret = false;
27918         try {
27919             ret = Roo.decode(response.responseText);
27920         } catch (e) {
27921             ret = {
27922                 success: false,
27923                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
27924                 errors : []
27925             };
27926         }
27927         return ret;
27928         
27929     }
27930 });
27931
27932
27933 Roo.form.Action.Load = function(form, options){
27934     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
27935     this.reader = this.form.reader;
27936 };
27937
27938 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
27939     type : 'load',
27940
27941     run : function(){
27942         
27943         Roo.Ajax.request(Roo.apply(
27944                 this.createCallback(), {
27945                     method:this.getMethod(),
27946                     url:this.getUrl(false),
27947                     params:this.getParams()
27948         }));
27949     },
27950
27951     success : function(response){
27952         
27953         var result = this.processResponse(response);
27954         if(result === true || !result.success || !result.data){
27955             this.failureType = Roo.form.Action.LOAD_FAILURE;
27956             this.form.afterAction(this, false);
27957             return;
27958         }
27959         this.form.clearInvalid();
27960         this.form.setValues(result.data);
27961         this.form.afterAction(this, true);
27962     },
27963
27964     handleResponse : function(response){
27965         if(this.form.reader){
27966             var rs = this.form.reader.read(response);
27967             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
27968             return {
27969                 success : rs.success,
27970                 data : data
27971             };
27972         }
27973         return Roo.decode(response.responseText);
27974     }
27975 });
27976
27977 Roo.form.Action.ACTION_TYPES = {
27978     'load' : Roo.form.Action.Load,
27979     'submit' : Roo.form.Action.Submit
27980 };/*
27981  * Based on:
27982  * Ext JS Library 1.1.1
27983  * Copyright(c) 2006-2007, Ext JS, LLC.
27984  *
27985  * Originally Released Under LGPL - original licence link has changed is not relivant.
27986  *
27987  * Fork - LGPL
27988  * <script type="text/javascript">
27989  */
27990  
27991 /**
27992  * @class Roo.form.Layout
27993  * @extends Roo.Component
27994  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
27995  * @constructor
27996  * @param {Object} config Configuration options
27997  */
27998 Roo.form.Layout = function(config){
27999     var xitems = [];
28000     if (config.items) {
28001         xitems = config.items;
28002         delete config.items;
28003     }
28004     Roo.form.Layout.superclass.constructor.call(this, config);
28005     this.stack = [];
28006     Roo.each(xitems, this.addxtype, this);
28007      
28008 };
28009
28010 Roo.extend(Roo.form.Layout, Roo.Component, {
28011     /**
28012      * @cfg {String/Object} autoCreate
28013      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
28014      */
28015     /**
28016      * @cfg {String/Object/Function} style
28017      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
28018      * a function which returns such a specification.
28019      */
28020     /**
28021      * @cfg {String} labelAlign
28022      * Valid values are "left," "top" and "right" (defaults to "left")
28023      */
28024     /**
28025      * @cfg {Number} labelWidth
28026      * Fixed width in pixels of all field labels (defaults to undefined)
28027      */
28028     /**
28029      * @cfg {Boolean} clear
28030      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
28031      */
28032     clear : true,
28033     /**
28034      * @cfg {String} labelSeparator
28035      * The separator to use after field labels (defaults to ':')
28036      */
28037     labelSeparator : ':',
28038     /**
28039      * @cfg {Boolean} hideLabels
28040      * True to suppress the display of field labels in this layout (defaults to false)
28041      */
28042     hideLabels : false,
28043
28044     // private
28045     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
28046     
28047     isLayout : true,
28048     
28049     // private
28050     onRender : function(ct, position){
28051         if(this.el){ // from markup
28052             this.el = Roo.get(this.el);
28053         }else {  // generate
28054             var cfg = this.getAutoCreate();
28055             this.el = ct.createChild(cfg, position);
28056         }
28057         if(this.style){
28058             this.el.applyStyles(this.style);
28059         }
28060         if(this.labelAlign){
28061             this.el.addClass('x-form-label-'+this.labelAlign);
28062         }
28063         if(this.hideLabels){
28064             this.labelStyle = "display:none";
28065             this.elementStyle = "padding-left:0;";
28066         }else{
28067             if(typeof this.labelWidth == 'number'){
28068                 this.labelStyle = "width:"+this.labelWidth+"px;";
28069                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
28070             }
28071             if(this.labelAlign == 'top'){
28072                 this.labelStyle = "width:auto;";
28073                 this.elementStyle = "padding-left:0;";
28074             }
28075         }
28076         var stack = this.stack;
28077         var slen = stack.length;
28078         if(slen > 0){
28079             if(!this.fieldTpl){
28080                 var t = new Roo.Template(
28081                     '<div class="x-form-item {5}">',
28082                         '<label for="{0}" style="{2}">{1}{4}</label>',
28083                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
28084                         '</div>',
28085                     '</div><div class="x-form-clear-left"></div>'
28086                 );
28087                 t.disableFormats = true;
28088                 t.compile();
28089                 Roo.form.Layout.prototype.fieldTpl = t;
28090             }
28091             for(var i = 0; i < slen; i++) {
28092                 if(stack[i].isFormField){
28093                     this.renderField(stack[i]);
28094                 }else{
28095                     this.renderComponent(stack[i]);
28096                 }
28097             }
28098         }
28099         if(this.clear){
28100             this.el.createChild({cls:'x-form-clear'});
28101         }
28102     },
28103
28104     // private
28105     renderField : function(f){
28106         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
28107                f.id, //0
28108                f.fieldLabel, //1
28109                f.labelStyle||this.labelStyle||'', //2
28110                this.elementStyle||'', //3
28111                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
28112                f.itemCls||this.itemCls||''  //5
28113        ], true).getPrevSibling());
28114     },
28115
28116     // private
28117     renderComponent : function(c){
28118         c.render(c.isLayout ? this.el : this.el.createChild());    
28119     },
28120     /**
28121      * Adds a object form elements (using the xtype property as the factory method.)
28122      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
28123      * @param {Object} config 
28124      */
28125     addxtype : function(o)
28126     {
28127         // create the lement.
28128         o.form = this.form;
28129         var fe = Roo.factory(o, Roo.form);
28130         this.form.allItems.push(fe);
28131         this.stack.push(fe);
28132         
28133         if (fe.isFormField) {
28134             this.form.items.add(fe);
28135         }
28136          
28137         return fe;
28138     }
28139 });
28140
28141 /**
28142  * @class Roo.form.Column
28143  * @extends Roo.form.Layout
28144  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
28145  * @constructor
28146  * @param {Object} config Configuration options
28147  */
28148 Roo.form.Column = function(config){
28149     Roo.form.Column.superclass.constructor.call(this, config);
28150 };
28151
28152 Roo.extend(Roo.form.Column, Roo.form.Layout, {
28153     /**
28154      * @cfg {Number/String} width
28155      * The fixed width of the column in pixels or CSS value (defaults to "auto")
28156      */
28157     /**
28158      * @cfg {String/Object} autoCreate
28159      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
28160      */
28161
28162     // private
28163     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
28164
28165     // private
28166     onRender : function(ct, position){
28167         Roo.form.Column.superclass.onRender.call(this, ct, position);
28168         if(this.width){
28169             this.el.setWidth(this.width);
28170         }
28171     }
28172 });
28173
28174
28175 /**
28176  * @class Roo.form.Row
28177  * @extends Roo.form.Layout
28178  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
28179  * @constructor
28180  * @param {Object} config Configuration options
28181  */
28182
28183  
28184 Roo.form.Row = function(config){
28185     Roo.form.Row.superclass.constructor.call(this, config);
28186 };
28187  
28188 Roo.extend(Roo.form.Row, Roo.form.Layout, {
28189       /**
28190      * @cfg {Number/String} width
28191      * The fixed width of the column in pixels or CSS value (defaults to "auto")
28192      */
28193     /**
28194      * @cfg {Number/String} height
28195      * The fixed height of the column in pixels or CSS value (defaults to "auto")
28196      */
28197     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
28198     
28199     padWidth : 20,
28200     // private
28201     onRender : function(ct, position){
28202         //console.log('row render');
28203         if(!this.rowTpl){
28204             var t = new Roo.Template(
28205                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
28206                     '<label for="{0}" style="{2}">{1}{4}</label>',
28207                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
28208                     '</div>',
28209                 '</div>'
28210             );
28211             t.disableFormats = true;
28212             t.compile();
28213             Roo.form.Layout.prototype.rowTpl = t;
28214         }
28215         this.fieldTpl = this.rowTpl;
28216         
28217         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
28218         var labelWidth = 100;
28219         
28220         if ((this.labelAlign != 'top')) {
28221             if (typeof this.labelWidth == 'number') {
28222                 labelWidth = this.labelWidth
28223             }
28224             this.padWidth =  20 + labelWidth;
28225             
28226         }
28227         
28228         Roo.form.Column.superclass.onRender.call(this, ct, position);
28229         if(this.width){
28230             this.el.setWidth(this.width);
28231         }
28232         if(this.height){
28233             this.el.setHeight(this.height);
28234         }
28235     },
28236     
28237     // private
28238     renderField : function(f){
28239         f.fieldEl = this.fieldTpl.append(this.el, [
28240                f.id, f.fieldLabel,
28241                f.labelStyle||this.labelStyle||'',
28242                this.elementStyle||'',
28243                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
28244                f.itemCls||this.itemCls||'',
28245                f.width ? f.width + this.padWidth : 160 + this.padWidth
28246        ],true);
28247     }
28248 });
28249  
28250
28251 /**
28252  * @class Roo.form.FieldSet
28253  * @extends Roo.form.Layout
28254  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
28255  * @constructor
28256  * @param {Object} config Configuration options
28257  */
28258 Roo.form.FieldSet = function(config){
28259     Roo.form.FieldSet.superclass.constructor.call(this, config);
28260 };
28261
28262 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
28263     /**
28264      * @cfg {String} legend
28265      * The text to display as the legend for the FieldSet (defaults to '')
28266      */
28267     /**
28268      * @cfg {String/Object} autoCreate
28269      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
28270      */
28271
28272     // private
28273     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
28274
28275     // private
28276     onRender : function(ct, position){
28277         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
28278         if(this.legend){
28279             this.setLegend(this.legend);
28280         }
28281     },
28282
28283     // private
28284     setLegend : function(text){
28285         if(this.rendered){
28286             this.el.child('legend').update(text);
28287         }
28288     }
28289 });/*
28290  * Based on:
28291  * Ext JS Library 1.1.1
28292  * Copyright(c) 2006-2007, Ext JS, LLC.
28293  *
28294  * Originally Released Under LGPL - original licence link has changed is not relivant.
28295  *
28296  * Fork - LGPL
28297  * <script type="text/javascript">
28298  */
28299 /**
28300  * @class Roo.form.VTypes
28301  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
28302  * @singleton
28303  */
28304 Roo.form.VTypes = function(){
28305     // closure these in so they are only created once.
28306     var alpha = /^[a-zA-Z_]+$/;
28307     var alphanum = /^[a-zA-Z0-9_]+$/;
28308     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
28309     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
28310
28311     // All these messages and functions are configurable
28312     return {
28313         /**
28314          * The function used to validate email addresses
28315          * @param {String} value The email address
28316          */
28317         'email' : function(v){
28318             return email.test(v);
28319         },
28320         /**
28321          * The error text to display when the email validation function returns false
28322          * @type String
28323          */
28324         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
28325         /**
28326          * The keystroke filter mask to be applied on email input
28327          * @type RegExp
28328          */
28329         'emailMask' : /[a-z0-9_\.\-@]/i,
28330
28331         /**
28332          * The function used to validate URLs
28333          * @param {String} value The URL
28334          */
28335         'url' : function(v){
28336             return url.test(v);
28337         },
28338         /**
28339          * The error text to display when the url validation function returns false
28340          * @type String
28341          */
28342         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
28343         
28344         /**
28345          * The function used to validate alpha values
28346          * @param {String} value The value
28347          */
28348         'alpha' : function(v){
28349             return alpha.test(v);
28350         },
28351         /**
28352          * The error text to display when the alpha validation function returns false
28353          * @type String
28354          */
28355         'alphaText' : 'This field should only contain letters and _',
28356         /**
28357          * The keystroke filter mask to be applied on alpha input
28358          * @type RegExp
28359          */
28360         'alphaMask' : /[a-z_]/i,
28361
28362         /**
28363          * The function used to validate alphanumeric values
28364          * @param {String} value The value
28365          */
28366         'alphanum' : function(v){
28367             return alphanum.test(v);
28368         },
28369         /**
28370          * The error text to display when the alphanumeric validation function returns false
28371          * @type String
28372          */
28373         'alphanumText' : 'This field should only contain letters, numbers and _',
28374         /**
28375          * The keystroke filter mask to be applied on alphanumeric input
28376          * @type RegExp
28377          */
28378         'alphanumMask' : /[a-z0-9_]/i
28379     };
28380 }();//<script type="text/javascript">
28381
28382 /**
28383  * @class Roo.form.FCKeditor
28384  * @extends Roo.form.TextArea
28385  * Wrapper around the FCKEditor http://www.fckeditor.net
28386  * @constructor
28387  * Creates a new FCKeditor
28388  * @param {Object} config Configuration options
28389  */
28390 Roo.form.FCKeditor = function(config){
28391     Roo.form.FCKeditor.superclass.constructor.call(this, config);
28392     this.addEvents({
28393          /**
28394          * @event editorinit
28395          * Fired when the editor is initialized - you can add extra handlers here..
28396          * @param {FCKeditor} this
28397          * @param {Object} the FCK object.
28398          */
28399         editorinit : true
28400     });
28401     
28402     
28403 };
28404 Roo.form.FCKeditor.editors = { };
28405 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
28406 {
28407     //defaultAutoCreate : {
28408     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
28409     //},
28410     // private
28411     /**
28412      * @cfg {Object} fck options - see fck manual for details.
28413      */
28414     fckconfig : false,
28415     
28416     /**
28417      * @cfg {Object} fck toolbar set (Basic or Default)
28418      */
28419     toolbarSet : 'Basic',
28420     /**
28421      * @cfg {Object} fck BasePath
28422      */ 
28423     basePath : '/fckeditor/',
28424     
28425     
28426     frame : false,
28427     
28428     value : '',
28429     
28430    
28431     onRender : function(ct, position)
28432     {
28433         if(!this.el){
28434             this.defaultAutoCreate = {
28435                 tag: "textarea",
28436                 style:"width:300px;height:60px;",
28437                 autocomplete: "off"
28438             };
28439         }
28440         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
28441         /*
28442         if(this.grow){
28443             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
28444             if(this.preventScrollbars){
28445                 this.el.setStyle("overflow", "hidden");
28446             }
28447             this.el.setHeight(this.growMin);
28448         }
28449         */
28450         //console.log('onrender' + this.getId() );
28451         Roo.form.FCKeditor.editors[this.getId()] = this;
28452          
28453
28454         this.replaceTextarea() ;
28455         
28456     },
28457     
28458     getEditor : function() {
28459         return this.fckEditor;
28460     },
28461     /**
28462      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
28463      * @param {Mixed} value The value to set
28464      */
28465     
28466     
28467     setValue : function(value)
28468     {
28469         //console.log('setValue: ' + value);
28470         
28471         if(typeof(value) == 'undefined') { // not sure why this is happending...
28472             return;
28473         }
28474         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
28475         
28476         //if(!this.el || !this.getEditor()) {
28477         //    this.value = value;
28478             //this.setValue.defer(100,this,[value]);    
28479         //    return;
28480         //} 
28481         
28482         if(!this.getEditor()) {
28483             return;
28484         }
28485         
28486         this.getEditor().SetData(value);
28487         
28488         //
28489
28490     },
28491
28492     /**
28493      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
28494      * @return {Mixed} value The field value
28495      */
28496     getValue : function()
28497     {
28498         
28499         if (this.frame && this.frame.dom.style.display == 'none') {
28500             return Roo.form.FCKeditor.superclass.getValue.call(this);
28501         }
28502         
28503         if(!this.el || !this.getEditor()) {
28504            
28505            // this.getValue.defer(100,this); 
28506             return this.value;
28507         }
28508        
28509         
28510         var value=this.getEditor().GetData();
28511         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
28512         return Roo.form.FCKeditor.superclass.getValue.call(this);
28513         
28514
28515     },
28516
28517     /**
28518      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
28519      * @return {Mixed} value The field value
28520      */
28521     getRawValue : function()
28522     {
28523         if (this.frame && this.frame.dom.style.display == 'none') {
28524             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
28525         }
28526         
28527         if(!this.el || !this.getEditor()) {
28528             //this.getRawValue.defer(100,this); 
28529             return this.value;
28530             return;
28531         }
28532         
28533         
28534         
28535         var value=this.getEditor().GetData();
28536         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
28537         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
28538          
28539     },
28540     
28541     setSize : function(w,h) {
28542         
28543         
28544         
28545         //if (this.frame && this.frame.dom.style.display == 'none') {
28546         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
28547         //    return;
28548         //}
28549         //if(!this.el || !this.getEditor()) {
28550         //    this.setSize.defer(100,this, [w,h]); 
28551         //    return;
28552         //}
28553         
28554         
28555         
28556         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
28557         
28558         this.frame.dom.setAttribute('width', w);
28559         this.frame.dom.setAttribute('height', h);
28560         this.frame.setSize(w,h);
28561         
28562     },
28563     
28564     toggleSourceEdit : function(value) {
28565         
28566       
28567          
28568         this.el.dom.style.display = value ? '' : 'none';
28569         this.frame.dom.style.display = value ?  'none' : '';
28570         
28571     },
28572     
28573     
28574     focus: function(tag)
28575     {
28576         if (this.frame.dom.style.display == 'none') {
28577             return Roo.form.FCKeditor.superclass.focus.call(this);
28578         }
28579         if(!this.el || !this.getEditor()) {
28580             this.focus.defer(100,this, [tag]); 
28581             return;
28582         }
28583         
28584         
28585         
28586         
28587         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
28588         this.getEditor().Focus();
28589         if (tgs.length) {
28590             if (!this.getEditor().Selection.GetSelection()) {
28591                 this.focus.defer(100,this, [tag]); 
28592                 return;
28593             }
28594             
28595             
28596             var r = this.getEditor().EditorDocument.createRange();
28597             r.setStart(tgs[0],0);
28598             r.setEnd(tgs[0],0);
28599             this.getEditor().Selection.GetSelection().removeAllRanges();
28600             this.getEditor().Selection.GetSelection().addRange(r);
28601             this.getEditor().Focus();
28602         }
28603         
28604     },
28605     
28606     
28607     
28608     replaceTextarea : function()
28609     {
28610         if ( document.getElementById( this.getId() + '___Frame' ) )
28611             return ;
28612         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
28613         //{
28614             // We must check the elements firstly using the Id and then the name.
28615         var oTextarea = document.getElementById( this.getId() );
28616         
28617         var colElementsByName = document.getElementsByName( this.getId() ) ;
28618          
28619         oTextarea.style.display = 'none' ;
28620
28621         if ( oTextarea.tabIndex ) {            
28622             this.TabIndex = oTextarea.tabIndex ;
28623         }
28624         
28625         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
28626         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
28627         this.frame = Roo.get(this.getId() + '___Frame')
28628     },
28629     
28630     _getConfigHtml : function()
28631     {
28632         var sConfig = '' ;
28633
28634         for ( var o in this.fckconfig ) {
28635             sConfig += sConfig.length > 0  ? '&amp;' : '';
28636             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
28637         }
28638
28639         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
28640     },
28641     
28642     
28643     _getIFrameHtml : function()
28644     {
28645         var sFile = 'fckeditor.html' ;
28646         /* no idea what this is about..
28647         try
28648         {
28649             if ( (/fcksource=true/i).test( window.top.location.search ) )
28650                 sFile = 'fckeditor.original.html' ;
28651         }
28652         catch (e) { 
28653         */
28654
28655         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
28656         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
28657         
28658         
28659         var html = '<iframe id="' + this.getId() +
28660             '___Frame" src="' + sLink +
28661             '" width="' + this.width +
28662             '" height="' + this.height + '"' +
28663             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
28664             ' frameborder="0" scrolling="no"></iframe>' ;
28665
28666         return html ;
28667     },
28668     
28669     _insertHtmlBefore : function( html, element )
28670     {
28671         if ( element.insertAdjacentHTML )       {
28672             // IE
28673             element.insertAdjacentHTML( 'beforeBegin', html ) ;
28674         } else { // Gecko
28675             var oRange = document.createRange() ;
28676             oRange.setStartBefore( element ) ;
28677             var oFragment = oRange.createContextualFragment( html );
28678             element.parentNode.insertBefore( oFragment, element ) ;
28679         }
28680     }
28681     
28682     
28683   
28684     
28685     
28686     
28687     
28688
28689 });
28690
28691 //Roo.reg('fckeditor', Roo.form.FCKeditor);
28692
28693 function FCKeditor_OnComplete(editorInstance){
28694     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
28695     f.fckEditor = editorInstance;
28696     //console.log("loaded");
28697     f.fireEvent('editorinit', f, editorInstance);
28698
28699   
28700
28701  
28702
28703
28704
28705
28706
28707
28708
28709
28710
28711
28712
28713
28714
28715
28716
28717 //<script type="text/javascript">
28718 /**
28719  * @class Roo.form.GridField
28720  * @extends Roo.form.Field
28721  * Embed a grid (or editable grid into a form)
28722  * STATUS ALPHA
28723  * 
28724  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
28725  * it needs 
28726  * xgrid.store = Roo.data.Store
28727  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
28728  * xgrid.store.reader = Roo.data.JsonReader 
28729  * 
28730  * 
28731  * @constructor
28732  * Creates a new GridField
28733  * @param {Object} config Configuration options
28734  */
28735 Roo.form.GridField = function(config){
28736     Roo.form.GridField.superclass.constructor.call(this, config);
28737      
28738 };
28739
28740 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
28741     /**
28742      * @cfg {Number} width  - used to restrict width of grid..
28743      */
28744     width : 100,
28745     /**
28746      * @cfg {Number} height - used to restrict height of grid..
28747      */
28748     height : 50,
28749      /**
28750      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
28751          * 
28752          *}
28753      */
28754     xgrid : false, 
28755     /**
28756      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28757      * {tag: "input", type: "checkbox", autocomplete: "off"})
28758      */
28759    // defaultAutoCreate : { tag: 'div' },
28760     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
28761     /**
28762      * @cfg {String} addTitle Text to include for adding a title.
28763      */
28764     addTitle : false,
28765     //
28766     onResize : function(){
28767         Roo.form.Field.superclass.onResize.apply(this, arguments);
28768     },
28769
28770     initEvents : function(){
28771         // Roo.form.Checkbox.superclass.initEvents.call(this);
28772         // has no events...
28773        
28774     },
28775
28776
28777     getResizeEl : function(){
28778         return this.wrap;
28779     },
28780
28781     getPositionEl : function(){
28782         return this.wrap;
28783     },
28784
28785     // private
28786     onRender : function(ct, position){
28787         
28788         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
28789         var style = this.style;
28790         delete this.style;
28791         
28792         Roo.form.GridField.superclass.onRender.call(this, ct, position);
28793         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
28794         this.viewEl = this.wrap.createChild({ tag: 'div' });
28795         if (style) {
28796             this.viewEl.applyStyles(style);
28797         }
28798         if (this.width) {
28799             this.viewEl.setWidth(this.width);
28800         }
28801         if (this.height) {
28802             this.viewEl.setHeight(this.height);
28803         }
28804         //if(this.inputValue !== undefined){
28805         //this.setValue(this.value);
28806         
28807         
28808         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
28809         
28810         
28811         this.grid.render();
28812         this.grid.getDataSource().on('remove', this.refreshValue, this);
28813         this.grid.getDataSource().on('update', this.refreshValue, this);
28814         this.grid.on('afteredit', this.refreshValue, this);
28815  
28816     },
28817      
28818     
28819     /**
28820      * Sets the value of the item. 
28821      * @param {String} either an object  or a string..
28822      */
28823     setValue : function(v){
28824         //this.value = v;
28825         v = v || []; // empty set..
28826         // this does not seem smart - it really only affects memoryproxy grids..
28827         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
28828             var ds = this.grid.getDataSource();
28829             // assumes a json reader..
28830             var data = {}
28831             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
28832             ds.loadData( data);
28833         }
28834         // clear selection so it does not get stale.
28835         if (this.grid.sm) { 
28836             this.grid.sm.clearSelections();
28837         }
28838         
28839         Roo.form.GridField.superclass.setValue.call(this, v);
28840         this.refreshValue();
28841         // should load data in the grid really....
28842     },
28843     
28844     // private
28845     refreshValue: function() {
28846          var val = [];
28847         this.grid.getDataSource().each(function(r) {
28848             val.push(r.data);
28849         });
28850         this.el.dom.value = Roo.encode(val);
28851     }
28852     
28853      
28854     
28855     
28856 });/*
28857  * Based on:
28858  * Ext JS Library 1.1.1
28859  * Copyright(c) 2006-2007, Ext JS, LLC.
28860  *
28861  * Originally Released Under LGPL - original licence link has changed is not relivant.
28862  *
28863  * Fork - LGPL
28864  * <script type="text/javascript">
28865  */
28866 /**
28867  * @class Roo.form.DisplayField
28868  * @extends Roo.form.Field
28869  * A generic Field to display non-editable data.
28870  * @constructor
28871  * Creates a new Display Field item.
28872  * @param {Object} config Configuration options
28873  */
28874 Roo.form.DisplayField = function(config){
28875     Roo.form.DisplayField.superclass.constructor.call(this, config);
28876     
28877 };
28878
28879 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
28880     inputType:      'hidden',
28881     allowBlank:     true,
28882     readOnly:         true,
28883     
28884  
28885     /**
28886      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
28887      */
28888     focusClass : undefined,
28889     /**
28890      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
28891      */
28892     fieldClass: 'x-form-field',
28893     
28894      /**
28895      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
28896      */
28897     valueRenderer: undefined,
28898     
28899     width: 100,
28900     /**
28901      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28902      * {tag: "input", type: "checkbox", autocomplete: "off"})
28903      */
28904      
28905  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
28906
28907     onResize : function(){
28908         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
28909         
28910     },
28911
28912     initEvents : function(){
28913         // Roo.form.Checkbox.superclass.initEvents.call(this);
28914         // has no events...
28915        
28916     },
28917
28918
28919     getResizeEl : function(){
28920         return this.wrap;
28921     },
28922
28923     getPositionEl : function(){
28924         return this.wrap;
28925     },
28926
28927     // private
28928     onRender : function(ct, position){
28929         
28930         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
28931         //if(this.inputValue !== undefined){
28932         this.wrap = this.el.wrap();
28933         
28934         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
28935         
28936         if (this.bodyStyle) {
28937             this.viewEl.applyStyles(this.bodyStyle);
28938         }
28939         //this.viewEl.setStyle('padding', '2px');
28940         
28941         this.setValue(this.value);
28942         
28943     },
28944 /*
28945     // private
28946     initValue : Roo.emptyFn,
28947
28948   */
28949
28950         // private
28951     onClick : function(){
28952         
28953     },
28954
28955     /**
28956      * Sets the checked state of the checkbox.
28957      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
28958      */
28959     setValue : function(v){
28960         this.value = v;
28961         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
28962         // this might be called before we have a dom element..
28963         if (!this.viewEl) {
28964             return;
28965         }
28966         this.viewEl.dom.innerHTML = html;
28967         Roo.form.DisplayField.superclass.setValue.call(this, v);
28968
28969     }
28970 });/*
28971  * 
28972  * Licence- LGPL
28973  * 
28974  */
28975
28976 /**
28977  * @class Roo.form.DayPicker
28978  * @extends Roo.form.Field
28979  * A Day picker show [M] [T] [W] ....
28980  * @constructor
28981  * Creates a new Day Picker
28982  * @param {Object} config Configuration options
28983  */
28984 Roo.form.DayPicker= function(config){
28985     Roo.form.DayPicker.superclass.constructor.call(this, config);
28986      
28987 };
28988
28989 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
28990     /**
28991      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
28992      */
28993     focusClass : undefined,
28994     /**
28995      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
28996      */
28997     fieldClass: "x-form-field",
28998    
28999     /**
29000      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
29001      * {tag: "input", type: "checkbox", autocomplete: "off"})
29002      */
29003     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
29004     
29005    
29006     actionMode : 'viewEl', 
29007     //
29008     // private
29009  
29010     inputType : 'hidden',
29011     
29012      
29013     inputElement: false, // real input element?
29014     basedOn: false, // ????
29015     
29016     isFormField: true, // not sure where this is needed!!!!
29017
29018     onResize : function(){
29019         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
29020         if(!this.boxLabel){
29021             this.el.alignTo(this.wrap, 'c-c');
29022         }
29023     },
29024
29025     initEvents : function(){
29026         Roo.form.Checkbox.superclass.initEvents.call(this);
29027         this.el.on("click", this.onClick,  this);
29028         this.el.on("change", this.onClick,  this);
29029     },
29030
29031
29032     getResizeEl : function(){
29033         return this.wrap;
29034     },
29035
29036     getPositionEl : function(){
29037         return this.wrap;
29038     },
29039
29040     
29041     // private
29042     onRender : function(ct, position){
29043         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
29044        
29045         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
29046         
29047         var r1 = '<table><tr>';
29048         var r2 = '<tr class="x-form-daypick-icons">';
29049         for (var i=0; i < 7; i++) {
29050             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
29051             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
29052         }
29053         
29054         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
29055         viewEl.select('img').on('click', this.onClick, this);
29056         this.viewEl = viewEl;   
29057         
29058         
29059         // this will not work on Chrome!!!
29060         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
29061         this.el.on('propertychange', this.setFromHidden,  this);  //ie
29062         
29063         
29064           
29065
29066     },
29067
29068     // private
29069     initValue : Roo.emptyFn,
29070
29071     /**
29072      * Returns the checked state of the checkbox.
29073      * @return {Boolean} True if checked, else false
29074      */
29075     getValue : function(){
29076         return this.el.dom.value;
29077         
29078     },
29079
29080         // private
29081     onClick : function(e){ 
29082         //this.setChecked(!this.checked);
29083         Roo.get(e.target).toggleClass('x-menu-item-checked');
29084         this.refreshValue();
29085         //if(this.el.dom.checked != this.checked){
29086         //    this.setValue(this.el.dom.checked);
29087        // }
29088     },
29089     
29090     // private
29091     refreshValue : function()
29092     {
29093         var val = '';
29094         this.viewEl.select('img',true).each(function(e,i,n)  {
29095             val += e.is(".x-menu-item-checked") ? String(n) : '';
29096         });
29097         this.setValue(val, true);
29098     },
29099
29100     /**
29101      * Sets the checked state of the checkbox.
29102      * On is always based on a string comparison between inputValue and the param.
29103      * @param {Boolean/String} value - the value to set 
29104      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
29105      */
29106     setValue : function(v,suppressEvent){
29107         if (!this.el.dom) {
29108             return;
29109         }
29110         var old = this.el.dom.value ;
29111         this.el.dom.value = v;
29112         if (suppressEvent) {
29113             return ;
29114         }
29115          
29116         // update display..
29117         this.viewEl.select('img',true).each(function(e,i,n)  {
29118             
29119             var on = e.is(".x-menu-item-checked");
29120             var newv = v.indexOf(String(n)) > -1;
29121             if (on != newv) {
29122                 e.toggleClass('x-menu-item-checked');
29123             }
29124             
29125         });
29126         
29127         
29128         this.fireEvent('change', this, v, old);
29129         
29130         
29131     },
29132    
29133     // handle setting of hidden value by some other method!!?!?
29134     setFromHidden: function()
29135     {
29136         if(!this.el){
29137             return;
29138         }
29139         //console.log("SET FROM HIDDEN");
29140         //alert('setFrom hidden');
29141         this.setValue(this.el.dom.value);
29142     },
29143     
29144     onDestroy : function()
29145     {
29146         if(this.viewEl){
29147             Roo.get(this.viewEl).remove();
29148         }
29149          
29150         Roo.form.DayPicker.superclass.onDestroy.call(this);
29151     }
29152
29153 });/*
29154  * RooJS Library 1.1.1
29155  * Copyright(c) 2008-2011  Alan Knowles
29156  *
29157  * License - LGPL
29158  */
29159  
29160
29161 /**
29162  * @class Roo.form.ComboCheck
29163  * @extends Roo.form.ComboBox
29164  * A combobox for multiple select items.
29165  *
29166  * FIXME - could do with a reset button..
29167  * 
29168  * @constructor
29169  * Create a new ComboCheck
29170  * @param {Object} config Configuration options
29171  */
29172 Roo.form.ComboCheck = function(config){
29173     Roo.form.ComboCheck.superclass.constructor.call(this, config);
29174     // should verify some data...
29175     // like
29176     // hiddenName = required..
29177     // displayField = required
29178     // valudField == required
29179     var req= [ 'hiddenName', 'displayField', 'valueField' ];
29180     var _t = this;
29181     Roo.each(req, function(e) {
29182         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
29183             throw "Roo.form.ComboCheck : missing value for: " + e;
29184         }
29185     });
29186     
29187     
29188 };
29189
29190 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
29191      
29192      
29193     editable : false,
29194      
29195     selectedClass: 'x-menu-item-checked', 
29196     
29197     // private
29198     onRender : function(ct, position){
29199         var _t = this;
29200         
29201         
29202         
29203         if(!this.tpl){
29204             var cls = 'x-combo-list';
29205
29206             
29207             this.tpl =  new Roo.Template({
29208                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
29209                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
29210                    '<span>{' + this.displayField + '}</span>' +
29211                     '</div>' 
29212                 
29213             });
29214         }
29215  
29216         
29217         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
29218         this.view.singleSelect = false;
29219         this.view.multiSelect = true;
29220         this.view.toggleSelect = true;
29221         this.pageTb.add(new Roo.Toolbar.Fill(), {
29222             
29223             text: 'Done',
29224             handler: function()
29225             {
29226                 _t.collapse();
29227             }
29228         });
29229     },
29230     
29231     onViewOver : function(e, t){
29232         // do nothing...
29233         return;
29234         
29235     },
29236     
29237     onViewClick : function(doFocus,index){
29238         return;
29239         
29240     },
29241     select: function () {
29242         //Roo.log("SELECT CALLED");
29243     },
29244      
29245     selectByValue : function(xv, scrollIntoView){
29246         var ar = this.getValueArray();
29247         var sels = [];
29248         
29249         Roo.each(ar, function(v) {
29250             if(v === undefined || v === null){
29251                 return;
29252             }
29253             var r = this.findRecord(this.valueField, v);
29254             if(r){
29255                 sels.push(this.store.indexOf(r))
29256                 
29257             }
29258         },this);
29259         this.view.select(sels);
29260         return false;
29261     },
29262     
29263     
29264     
29265     onSelect : function(record, index){
29266        // Roo.log("onselect Called");
29267        // this is only called by the clear button now..
29268         this.view.clearSelections();
29269         this.setValue('[]');
29270         if (this.value != this.valueBefore) {
29271             this.fireEvent('change', this, this.value, this.valueBefore);
29272         }
29273     },
29274     getValueArray : function()
29275     {
29276         var ar = [] ;
29277         
29278         try {
29279             //Roo.log(this.value);
29280             if (typeof(this.value) == 'undefined') {
29281                 return [];
29282             }
29283             var ar = Roo.decode(this.value);
29284             return  ar instanceof Array ? ar : []; //?? valid?
29285             
29286         } catch(e) {
29287             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
29288             return [];
29289         }
29290          
29291     },
29292     expand : function ()
29293     {
29294         Roo.form.ComboCheck.superclass.expand.call(this);
29295         this.valueBefore = this.value;
29296         
29297
29298     },
29299     
29300     collapse : function(){
29301         Roo.form.ComboCheck.superclass.collapse.call(this);
29302         var sl = this.view.getSelectedIndexes();
29303         var st = this.store;
29304         var nv = [];
29305         var tv = [];
29306         var r;
29307         Roo.each(sl, function(i) {
29308             r = st.getAt(i);
29309             nv.push(r.get(this.valueField));
29310         },this);
29311         this.setValue(Roo.encode(nv));
29312         if (this.value != this.valueBefore) {
29313
29314             this.fireEvent('change', this, this.value, this.valueBefore);
29315         }
29316         
29317     },
29318     
29319     setValue : function(v){
29320         // Roo.log(v);
29321         this.value = v;
29322         
29323         var vals = this.getValueArray();
29324         var tv = [];
29325         Roo.each(vals, function(k) {
29326             var r = this.findRecord(this.valueField, k);
29327             if(r){
29328                 tv.push(r.data[this.displayField]);
29329             }else if(this.valueNotFoundText !== undefined){
29330                 tv.push( this.valueNotFoundText );
29331             }
29332         },this);
29333        // Roo.log(tv);
29334         
29335         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
29336         this.hiddenField.value = v;
29337         this.value = v;
29338     }
29339     
29340 });//<script type="text/javasscript">
29341  
29342
29343 /**
29344  * @class Roo.DDView
29345  * A DnD enabled version of Roo.View.
29346  * @param {Element/String} container The Element in which to create the View.
29347  * @param {String} tpl The template string used to create the markup for each element of the View
29348  * @param {Object} config The configuration properties. These include all the config options of
29349  * {@link Roo.View} plus some specific to this class.<br>
29350  * <p>
29351  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
29352  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
29353  * <p>
29354  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
29355 .x-view-drag-insert-above {
29356         border-top:1px dotted #3366cc;
29357 }
29358 .x-view-drag-insert-below {
29359         border-bottom:1px dotted #3366cc;
29360 }
29361 </code></pre>
29362  * 
29363  */
29364  
29365 Roo.DDView = function(container, tpl, config) {
29366     Roo.DDView.superclass.constructor.apply(this, arguments);
29367     this.getEl().setStyle("outline", "0px none");
29368     this.getEl().unselectable();
29369     if (this.dragGroup) {
29370                 this.setDraggable(this.dragGroup.split(","));
29371     }
29372     if (this.dropGroup) {
29373                 this.setDroppable(this.dropGroup.split(","));
29374     }
29375     if (this.deletable) {
29376         this.setDeletable();
29377     }
29378     this.isDirtyFlag = false;
29379         this.addEvents({
29380                 "drop" : true
29381         });
29382 };
29383
29384 Roo.extend(Roo.DDView, Roo.View, {
29385 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
29386 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
29387 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
29388 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
29389
29390         isFormField: true,
29391
29392         reset: Roo.emptyFn,
29393         
29394         clearInvalid: Roo.form.Field.prototype.clearInvalid,
29395
29396         validate: function() {
29397                 return true;
29398         },
29399         
29400         destroy: function() {
29401                 this.purgeListeners();
29402                 this.getEl.removeAllListeners();
29403                 this.getEl().remove();
29404                 if (this.dragZone) {
29405                         if (this.dragZone.destroy) {
29406                                 this.dragZone.destroy();
29407                         }
29408                 }
29409                 if (this.dropZone) {
29410                         if (this.dropZone.destroy) {
29411                                 this.dropZone.destroy();
29412                         }
29413                 }
29414         },
29415
29416 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
29417         getName: function() {
29418                 return this.name;
29419         },
29420
29421 /**     Loads the View from a JSON string representing the Records to put into the Store. */
29422         setValue: function(v) {
29423                 if (!this.store) {
29424                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
29425                 }
29426                 var data = {};
29427                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
29428                 this.store.proxy = new Roo.data.MemoryProxy(data);
29429                 this.store.load();
29430         },
29431
29432 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
29433         getValue: function() {
29434                 var result = '(';
29435                 this.store.each(function(rec) {
29436                         result += rec.id + ',';
29437                 });
29438                 return result.substr(0, result.length - 1) + ')';
29439         },
29440         
29441         getIds: function() {
29442                 var i = 0, result = new Array(this.store.getCount());
29443                 this.store.each(function(rec) {
29444                         result[i++] = rec.id;
29445                 });
29446                 return result;
29447         },
29448         
29449         isDirty: function() {
29450                 return this.isDirtyFlag;
29451         },
29452
29453 /**
29454  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
29455  *      whole Element becomes the target, and this causes the drop gesture to append.
29456  */
29457     getTargetFromEvent : function(e) {
29458                 var target = e.getTarget();
29459                 while ((target !== null) && (target.parentNode != this.el.dom)) {
29460                 target = target.parentNode;
29461                 }
29462                 if (!target) {
29463                         target = this.el.dom.lastChild || this.el.dom;
29464                 }
29465                 return target;
29466     },
29467
29468 /**
29469  *      Create the drag data which consists of an object which has the property "ddel" as
29470  *      the drag proxy element. 
29471  */
29472     getDragData : function(e) {
29473         var target = this.findItemFromChild(e.getTarget());
29474                 if(target) {
29475                         this.handleSelection(e);
29476                         var selNodes = this.getSelectedNodes();
29477             var dragData = {
29478                 source: this,
29479                 copy: this.copy || (this.allowCopy && e.ctrlKey),
29480                 nodes: selNodes,
29481                 records: []
29482                         };
29483                         var selectedIndices = this.getSelectedIndexes();
29484                         for (var i = 0; i < selectedIndices.length; i++) {
29485                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
29486                         }
29487                         if (selNodes.length == 1) {
29488                                 dragData.ddel = target.cloneNode(true); // the div element
29489                         } else {
29490                                 var div = document.createElement('div'); // create the multi element drag "ghost"
29491                                 div.className = 'multi-proxy';
29492                                 for (var i = 0, len = selNodes.length; i < len; i++) {
29493                                         div.appendChild(selNodes[i].cloneNode(true));
29494                                 }
29495                                 dragData.ddel = div;
29496                         }
29497             //console.log(dragData)
29498             //console.log(dragData.ddel.innerHTML)
29499                         return dragData;
29500                 }
29501         //console.log('nodragData')
29502                 return false;
29503     },
29504     
29505 /**     Specify to which ddGroup items in this DDView may be dragged. */
29506     setDraggable: function(ddGroup) {
29507         if (ddGroup instanceof Array) {
29508                 Roo.each(ddGroup, this.setDraggable, this);
29509                 return;
29510         }
29511         if (this.dragZone) {
29512                 this.dragZone.addToGroup(ddGroup);
29513         } else {
29514                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
29515                                 containerScroll: true,
29516                                 ddGroup: ddGroup 
29517
29518                         });
29519 //                      Draggability implies selection. DragZone's mousedown selects the element.
29520                         if (!this.multiSelect) { this.singleSelect = true; }
29521
29522 //                      Wire the DragZone's handlers up to methods in *this*
29523                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
29524                 }
29525     },
29526
29527 /**     Specify from which ddGroup this DDView accepts drops. */
29528     setDroppable: function(ddGroup) {
29529         if (ddGroup instanceof Array) {
29530                 Roo.each(ddGroup, this.setDroppable, this);
29531                 return;
29532         }
29533         if (this.dropZone) {
29534                 this.dropZone.addToGroup(ddGroup);
29535         } else {
29536                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
29537                                 containerScroll: true,
29538                                 ddGroup: ddGroup
29539                         });
29540
29541 //                      Wire the DropZone's handlers up to methods in *this*
29542                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
29543                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
29544                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
29545                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
29546                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
29547                 }
29548     },
29549
29550 /**     Decide whether to drop above or below a View node. */
29551     getDropPoint : function(e, n, dd){
29552         if (n == this.el.dom) { return "above"; }
29553                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
29554                 var c = t + (b - t) / 2;
29555                 var y = Roo.lib.Event.getPageY(e);
29556                 if(y <= c) {
29557                         return "above";
29558                 }else{
29559                         return "below";
29560                 }
29561     },
29562
29563     onNodeEnter : function(n, dd, e, data){
29564                 return false;
29565     },
29566     
29567     onNodeOver : function(n, dd, e, data){
29568                 var pt = this.getDropPoint(e, n, dd);
29569                 // set the insert point style on the target node
29570                 var dragElClass = this.dropNotAllowed;
29571                 if (pt) {
29572                         var targetElClass;
29573                         if (pt == "above"){
29574                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
29575                                 targetElClass = "x-view-drag-insert-above";
29576                         } else {
29577                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
29578                                 targetElClass = "x-view-drag-insert-below";
29579                         }
29580                         if (this.lastInsertClass != targetElClass){
29581                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
29582                                 this.lastInsertClass = targetElClass;
29583                         }
29584                 }
29585                 return dragElClass;
29586         },
29587
29588     onNodeOut : function(n, dd, e, data){
29589                 this.removeDropIndicators(n);
29590     },
29591
29592     onNodeDrop : function(n, dd, e, data){
29593         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
29594                 return false;
29595         }
29596         var pt = this.getDropPoint(e, n, dd);
29597                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
29598                 if (pt == "below") { insertAt++; }
29599                 for (var i = 0; i < data.records.length; i++) {
29600                         var r = data.records[i];
29601                         var dup = this.store.getById(r.id);
29602                         if (dup && (dd != this.dragZone)) {
29603                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
29604                         } else {
29605                                 if (data.copy) {
29606                                         this.store.insert(insertAt++, r.copy());
29607                                 } else {
29608                                         data.source.isDirtyFlag = true;
29609                                         r.store.remove(r);
29610                                         this.store.insert(insertAt++, r);
29611                                 }
29612                                 this.isDirtyFlag = true;
29613                         }
29614                 }
29615                 this.dragZone.cachedTarget = null;
29616                 return true;
29617     },
29618
29619     removeDropIndicators : function(n){
29620                 if(n){
29621                         Roo.fly(n).removeClass([
29622                                 "x-view-drag-insert-above",
29623                                 "x-view-drag-insert-below"]);
29624                         this.lastInsertClass = "_noclass";
29625                 }
29626     },
29627
29628 /**
29629  *      Utility method. Add a delete option to the DDView's context menu.
29630  *      @param {String} imageUrl The URL of the "delete" icon image.
29631  */
29632         setDeletable: function(imageUrl) {
29633                 if (!this.singleSelect && !this.multiSelect) {
29634                         this.singleSelect = true;
29635                 }
29636                 var c = this.getContextMenu();
29637                 this.contextMenu.on("itemclick", function(item) {
29638                         switch (item.id) {
29639                                 case "delete":
29640                                         this.remove(this.getSelectedIndexes());
29641                                         break;
29642                         }
29643                 }, this);
29644                 this.contextMenu.add({
29645                         icon: imageUrl,
29646                         id: "delete",
29647                         text: 'Delete'
29648                 });
29649         },
29650         
29651 /**     Return the context menu for this DDView. */
29652         getContextMenu: function() {
29653                 if (!this.contextMenu) {
29654 //                      Create the View's context menu
29655                         this.contextMenu = new Roo.menu.Menu({
29656                                 id: this.id + "-contextmenu"
29657                         });
29658                         this.el.on("contextmenu", this.showContextMenu, this);
29659                 }
29660                 return this.contextMenu;
29661         },
29662         
29663         disableContextMenu: function() {
29664                 if (this.contextMenu) {
29665                         this.el.un("contextmenu", this.showContextMenu, this);
29666                 }
29667         },
29668
29669         showContextMenu: function(e, item) {
29670         item = this.findItemFromChild(e.getTarget());
29671                 if (item) {
29672                         e.stopEvent();
29673                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
29674                         this.contextMenu.showAt(e.getXY());
29675             }
29676     },
29677
29678 /**
29679  *      Remove {@link Roo.data.Record}s at the specified indices.
29680  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
29681  */
29682     remove: function(selectedIndices) {
29683                 selectedIndices = [].concat(selectedIndices);
29684                 for (var i = 0; i < selectedIndices.length; i++) {
29685                         var rec = this.store.getAt(selectedIndices[i]);
29686                         this.store.remove(rec);
29687                 }
29688     },
29689
29690 /**
29691  *      Double click fires the event, but also, if this is draggable, and there is only one other
29692  *      related DropZone, it transfers the selected node.
29693  */
29694     onDblClick : function(e){
29695         var item = this.findItemFromChild(e.getTarget());
29696         if(item){
29697             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
29698                 return false;
29699             }
29700             if (this.dragGroup) {
29701                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
29702                     while (targets.indexOf(this.dropZone) > -1) {
29703                             targets.remove(this.dropZone);
29704                                 }
29705                     if (targets.length == 1) {
29706                                         this.dragZone.cachedTarget = null;
29707                         var el = Roo.get(targets[0].getEl());
29708                         var box = el.getBox(true);
29709                         targets[0].onNodeDrop(el.dom, {
29710                                 target: el.dom,
29711                                 xy: [box.x, box.y + box.height - 1]
29712                         }, null, this.getDragData(e));
29713                     }
29714                 }
29715         }
29716     },
29717     
29718     handleSelection: function(e) {
29719                 this.dragZone.cachedTarget = null;
29720         var item = this.findItemFromChild(e.getTarget());
29721         if (!item) {
29722                 this.clearSelections(true);
29723                 return;
29724         }
29725                 if (item && (this.multiSelect || this.singleSelect)){
29726                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
29727                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
29728                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
29729                                 this.unselect(item);
29730                         } else {
29731                                 this.select(item, this.multiSelect && e.ctrlKey);
29732                                 this.lastSelection = item;
29733                         }
29734                 }
29735     },
29736
29737     onItemClick : function(item, index, e){
29738                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
29739                         return false;
29740                 }
29741                 return true;
29742     },
29743
29744     unselect : function(nodeInfo, suppressEvent){
29745                 var node = this.getNode(nodeInfo);
29746                 if(node && this.isSelected(node)){
29747                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
29748                                 Roo.fly(node).removeClass(this.selectedClass);
29749                                 this.selections.remove(node);
29750                                 if(!suppressEvent){
29751                                         this.fireEvent("selectionchange", this, this.selections);
29752                                 }
29753                         }
29754                 }
29755     }
29756 });
29757 /*
29758  * Based on:
29759  * Ext JS Library 1.1.1
29760  * Copyright(c) 2006-2007, Ext JS, LLC.
29761  *
29762  * Originally Released Under LGPL - original licence link has changed is not relivant.
29763  *
29764  * Fork - LGPL
29765  * <script type="text/javascript">
29766  */
29767  
29768 /**
29769  * @class Roo.LayoutManager
29770  * @extends Roo.util.Observable
29771  * Base class for layout managers.
29772  */
29773 Roo.LayoutManager = function(container, config){
29774     Roo.LayoutManager.superclass.constructor.call(this);
29775     this.el = Roo.get(container);
29776     // ie scrollbar fix
29777     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
29778         document.body.scroll = "no";
29779     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
29780         this.el.position('relative');
29781     }
29782     this.id = this.el.id;
29783     this.el.addClass("x-layout-container");
29784     /** false to disable window resize monitoring @type Boolean */
29785     this.monitorWindowResize = true;
29786     this.regions = {};
29787     this.addEvents({
29788         /**
29789          * @event layout
29790          * Fires when a layout is performed. 
29791          * @param {Roo.LayoutManager} this
29792          */
29793         "layout" : true,
29794         /**
29795          * @event regionresized
29796          * Fires when the user resizes a region. 
29797          * @param {Roo.LayoutRegion} region The resized region
29798          * @param {Number} newSize The new size (width for east/west, height for north/south)
29799          */
29800         "regionresized" : true,
29801         /**
29802          * @event regioncollapsed
29803          * Fires when a region is collapsed. 
29804          * @param {Roo.LayoutRegion} region The collapsed region
29805          */
29806         "regioncollapsed" : true,
29807         /**
29808          * @event regionexpanded
29809          * Fires when a region is expanded.  
29810          * @param {Roo.LayoutRegion} region The expanded region
29811          */
29812         "regionexpanded" : true
29813     });
29814     this.updating = false;
29815     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
29816 };
29817
29818 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
29819     /**
29820      * Returns true if this layout is currently being updated
29821      * @return {Boolean}
29822      */
29823     isUpdating : function(){
29824         return this.updating; 
29825     },
29826     
29827     /**
29828      * Suspend the LayoutManager from doing auto-layouts while
29829      * making multiple add or remove calls
29830      */
29831     beginUpdate : function(){
29832         this.updating = true;    
29833     },
29834     
29835     /**
29836      * Restore auto-layouts and optionally disable the manager from performing a layout
29837      * @param {Boolean} noLayout true to disable a layout update 
29838      */
29839     endUpdate : function(noLayout){
29840         this.updating = false;
29841         if(!noLayout){
29842             this.layout();
29843         }    
29844     },
29845     
29846     layout: function(){
29847         
29848     },
29849     
29850     onRegionResized : function(region, newSize){
29851         this.fireEvent("regionresized", region, newSize);
29852         this.layout();
29853     },
29854     
29855     onRegionCollapsed : function(region){
29856         this.fireEvent("regioncollapsed", region);
29857     },
29858     
29859     onRegionExpanded : function(region){
29860         this.fireEvent("regionexpanded", region);
29861     },
29862         
29863     /**
29864      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
29865      * performs box-model adjustments.
29866      * @return {Object} The size as an object {width: (the width), height: (the height)}
29867      */
29868     getViewSize : function(){
29869         var size;
29870         if(this.el.dom != document.body){
29871             size = this.el.getSize();
29872         }else{
29873             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
29874         }
29875         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
29876         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
29877         return size;
29878     },
29879     
29880     /**
29881      * Returns the Element this layout is bound to.
29882      * @return {Roo.Element}
29883      */
29884     getEl : function(){
29885         return this.el;
29886     },
29887     
29888     /**
29889      * Returns the specified region.
29890      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
29891      * @return {Roo.LayoutRegion}
29892      */
29893     getRegion : function(target){
29894         return this.regions[target.toLowerCase()];
29895     },
29896     
29897     onWindowResize : function(){
29898         if(this.monitorWindowResize){
29899             this.layout();
29900         }
29901     }
29902 });/*
29903  * Based on:
29904  * Ext JS Library 1.1.1
29905  * Copyright(c) 2006-2007, Ext JS, LLC.
29906  *
29907  * Originally Released Under LGPL - original licence link has changed is not relivant.
29908  *
29909  * Fork - LGPL
29910  * <script type="text/javascript">
29911  */
29912 /**
29913  * @class Roo.BorderLayout
29914  * @extends Roo.LayoutManager
29915  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
29916  * please see: <br><br>
29917  * <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>
29918  * <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>
29919  * Example:
29920  <pre><code>
29921  var layout = new Roo.BorderLayout(document.body, {
29922     north: {
29923         initialSize: 25,
29924         titlebar: false
29925     },
29926     west: {
29927         split:true,
29928         initialSize: 200,
29929         minSize: 175,
29930         maxSize: 400,
29931         titlebar: true,
29932         collapsible: true
29933     },
29934     east: {
29935         split:true,
29936         initialSize: 202,
29937         minSize: 175,
29938         maxSize: 400,
29939         titlebar: true,
29940         collapsible: true
29941     },
29942     south: {
29943         split:true,
29944         initialSize: 100,
29945         minSize: 100,
29946         maxSize: 200,
29947         titlebar: true,
29948         collapsible: true
29949     },
29950     center: {
29951         titlebar: true,
29952         autoScroll:true,
29953         resizeTabs: true,
29954         minTabWidth: 50,
29955         preferredTabWidth: 150
29956     }
29957 });
29958
29959 // shorthand
29960 var CP = Roo.ContentPanel;
29961
29962 layout.beginUpdate();
29963 layout.add("north", new CP("north", "North"));
29964 layout.add("south", new CP("south", {title: "South", closable: true}));
29965 layout.add("west", new CP("west", {title: "West"}));
29966 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
29967 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
29968 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
29969 layout.getRegion("center").showPanel("center1");
29970 layout.endUpdate();
29971 </code></pre>
29972
29973 <b>The container the layout is rendered into can be either the body element or any other element.
29974 If it is not the body element, the container needs to either be an absolute positioned element,
29975 or you will need to add "position:relative" to the css of the container.  You will also need to specify
29976 the container size if it is not the body element.</b>
29977
29978 * @constructor
29979 * Create a new BorderLayout
29980 * @param {String/HTMLElement/Element} container The container this layout is bound to
29981 * @param {Object} config Configuration options
29982  */
29983 Roo.BorderLayout = function(container, config){
29984     config = config || {};
29985     Roo.BorderLayout.superclass.constructor.call(this, container, config);
29986     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
29987     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
29988         var target = this.factory.validRegions[i];
29989         if(config[target]){
29990             this.addRegion(target, config[target]);
29991         }
29992     }
29993 };
29994
29995 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
29996     /**
29997      * Creates and adds a new region if it doesn't already exist.
29998      * @param {String} target The target region key (north, south, east, west or center).
29999      * @param {Object} config The regions config object
30000      * @return {BorderLayoutRegion} The new region
30001      */
30002     addRegion : function(target, config){
30003         if(!this.regions[target]){
30004             var r = this.factory.create(target, this, config);
30005             this.bindRegion(target, r);
30006         }
30007         return this.regions[target];
30008     },
30009
30010     // private (kinda)
30011     bindRegion : function(name, r){
30012         this.regions[name] = r;
30013         r.on("visibilitychange", this.layout, this);
30014         r.on("paneladded", this.layout, this);
30015         r.on("panelremoved", this.layout, this);
30016         r.on("invalidated", this.layout, this);
30017         r.on("resized", this.onRegionResized, this);
30018         r.on("collapsed", this.onRegionCollapsed, this);
30019         r.on("expanded", this.onRegionExpanded, this);
30020     },
30021
30022     /**
30023      * Performs a layout update.
30024      */
30025     layout : function(){
30026         if(this.updating) return;
30027         var size = this.getViewSize();
30028         var w = size.width;
30029         var h = size.height;
30030         var centerW = w;
30031         var centerH = h;
30032         var centerY = 0;
30033         var centerX = 0;
30034         //var x = 0, y = 0;
30035
30036         var rs = this.regions;
30037         var north = rs["north"];
30038         var south = rs["south"]; 
30039         var west = rs["west"];
30040         var east = rs["east"];
30041         var center = rs["center"];
30042         //if(this.hideOnLayout){ // not supported anymore
30043             //c.el.setStyle("display", "none");
30044         //}
30045         if(north && north.isVisible()){
30046             var b = north.getBox();
30047             var m = north.getMargins();
30048             b.width = w - (m.left+m.right);
30049             b.x = m.left;
30050             b.y = m.top;
30051             centerY = b.height + b.y + m.bottom;
30052             centerH -= centerY;
30053             north.updateBox(this.safeBox(b));
30054         }
30055         if(south && south.isVisible()){
30056             var b = south.getBox();
30057             var m = south.getMargins();
30058             b.width = w - (m.left+m.right);
30059             b.x = m.left;
30060             var totalHeight = (b.height + m.top + m.bottom);
30061             b.y = h - totalHeight + m.top;
30062             centerH -= totalHeight;
30063             south.updateBox(this.safeBox(b));
30064         }
30065         if(west && west.isVisible()){
30066             var b = west.getBox();
30067             var m = west.getMargins();
30068             b.height = centerH - (m.top+m.bottom);
30069             b.x = m.left;
30070             b.y = centerY + m.top;
30071             var totalWidth = (b.width + m.left + m.right);
30072             centerX += totalWidth;
30073             centerW -= totalWidth;
30074             west.updateBox(this.safeBox(b));
30075         }
30076         if(east && east.isVisible()){
30077             var b = east.getBox();
30078             var m = east.getMargins();
30079             b.height = centerH - (m.top+m.bottom);
30080             var totalWidth = (b.width + m.left + m.right);
30081             b.x = w - totalWidth + m.left;
30082             b.y = centerY + m.top;
30083             centerW -= totalWidth;
30084             east.updateBox(this.safeBox(b));
30085         }
30086         if(center){
30087             var m = center.getMargins();
30088             var centerBox = {
30089                 x: centerX + m.left,
30090                 y: centerY + m.top,
30091                 width: centerW - (m.left+m.right),
30092                 height: centerH - (m.top+m.bottom)
30093             };
30094             //if(this.hideOnLayout){
30095                 //center.el.setStyle("display", "block");
30096             //}
30097             center.updateBox(this.safeBox(centerBox));
30098         }
30099         this.el.repaint();
30100         this.fireEvent("layout", this);
30101     },
30102
30103     // private
30104     safeBox : function(box){
30105         box.width = Math.max(0, box.width);
30106         box.height = Math.max(0, box.height);
30107         return box;
30108     },
30109
30110     /**
30111      * Adds a ContentPanel (or subclass) to this layout.
30112      * @param {String} target The target region key (north, south, east, west or center).
30113      * @param {Roo.ContentPanel} panel The panel to add
30114      * @return {Roo.ContentPanel} The added panel
30115      */
30116     add : function(target, panel){
30117          
30118         target = target.toLowerCase();
30119         return this.regions[target].add(panel);
30120     },
30121
30122     /**
30123      * Remove a ContentPanel (or subclass) to this layout.
30124      * @param {String} target The target region key (north, south, east, west or center).
30125      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
30126      * @return {Roo.ContentPanel} The removed panel
30127      */
30128     remove : function(target, panel){
30129         target = target.toLowerCase();
30130         return this.regions[target].remove(panel);
30131     },
30132
30133     /**
30134      * Searches all regions for a panel with the specified id
30135      * @param {String} panelId
30136      * @return {Roo.ContentPanel} The panel or null if it wasn't found
30137      */
30138     findPanel : function(panelId){
30139         var rs = this.regions;
30140         for(var target in rs){
30141             if(typeof rs[target] != "function"){
30142                 var p = rs[target].getPanel(panelId);
30143                 if(p){
30144                     return p;
30145                 }
30146             }
30147         }
30148         return null;
30149     },
30150
30151     /**
30152      * Searches all regions for a panel with the specified id and activates (shows) it.
30153      * @param {String/ContentPanel} panelId The panels id or the panel itself
30154      * @return {Roo.ContentPanel} The shown panel or null
30155      */
30156     showPanel : function(panelId) {
30157       var rs = this.regions;
30158       for(var target in rs){
30159          var r = rs[target];
30160          if(typeof r != "function"){
30161             if(r.hasPanel(panelId)){
30162                return r.showPanel(panelId);
30163             }
30164          }
30165       }
30166       return null;
30167    },
30168
30169    /**
30170      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
30171      * @param {Roo.state.Provider} provider (optional) An alternate state provider
30172      */
30173     restoreState : function(provider){
30174         if(!provider){
30175             provider = Roo.state.Manager;
30176         }
30177         var sm = new Roo.LayoutStateManager();
30178         sm.init(this, provider);
30179     },
30180
30181     /**
30182      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
30183      * object should contain properties for each region to add ContentPanels to, and each property's value should be
30184      * a valid ContentPanel config object.  Example:
30185      * <pre><code>
30186 // Create the main layout
30187 var layout = new Roo.BorderLayout('main-ct', {
30188     west: {
30189         split:true,
30190         minSize: 175,
30191         titlebar: true
30192     },
30193     center: {
30194         title:'Components'
30195     }
30196 }, 'main-ct');
30197
30198 // Create and add multiple ContentPanels at once via configs
30199 layout.batchAdd({
30200    west: {
30201        id: 'source-files',
30202        autoCreate:true,
30203        title:'Ext Source Files',
30204        autoScroll:true,
30205        fitToFrame:true
30206    },
30207    center : {
30208        el: cview,
30209        autoScroll:true,
30210        fitToFrame:true,
30211        toolbar: tb,
30212        resizeEl:'cbody'
30213    }
30214 });
30215 </code></pre>
30216      * @param {Object} regions An object containing ContentPanel configs by region name
30217      */
30218     batchAdd : function(regions){
30219         this.beginUpdate();
30220         for(var rname in regions){
30221             var lr = this.regions[rname];
30222             if(lr){
30223                 this.addTypedPanels(lr, regions[rname]);
30224             }
30225         }
30226         this.endUpdate();
30227     },
30228
30229     // private
30230     addTypedPanels : function(lr, ps){
30231         if(typeof ps == 'string'){
30232             lr.add(new Roo.ContentPanel(ps));
30233         }
30234         else if(ps instanceof Array){
30235             for(var i =0, len = ps.length; i < len; i++){
30236                 this.addTypedPanels(lr, ps[i]);
30237             }
30238         }
30239         else if(!ps.events){ // raw config?
30240             var el = ps.el;
30241             delete ps.el; // prevent conflict
30242             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
30243         }
30244         else {  // panel object assumed!
30245             lr.add(ps);
30246         }
30247     },
30248     /**
30249      * Adds a xtype elements to the layout.
30250      * <pre><code>
30251
30252 layout.addxtype({
30253        xtype : 'ContentPanel',
30254        region: 'west',
30255        items: [ .... ]
30256    }
30257 );
30258
30259 layout.addxtype({
30260         xtype : 'NestedLayoutPanel',
30261         region: 'west',
30262         layout: {
30263            center: { },
30264            west: { }   
30265         },
30266         items : [ ... list of content panels or nested layout panels.. ]
30267    }
30268 );
30269 </code></pre>
30270      * @param {Object} cfg Xtype definition of item to add.
30271      */
30272     addxtype : function(cfg)
30273     {
30274         // basically accepts a pannel...
30275         // can accept a layout region..!?!?
30276         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
30277         
30278         if (!cfg.xtype.match(/Panel$/)) {
30279             return false;
30280         }
30281         var ret = false;
30282         
30283         if (typeof(cfg.region) == 'undefined') {
30284             Roo.log("Failed to add Panel, region was not set");
30285             Roo.log(cfg);
30286             return false;
30287         }
30288         var region = cfg.region;
30289         delete cfg.region;
30290         
30291           
30292         var xitems = [];
30293         if (cfg.items) {
30294             xitems = cfg.items;
30295             delete cfg.items;
30296         }
30297         var nb = false;
30298         
30299         switch(cfg.xtype) 
30300         {
30301             case 'ContentPanel':  // ContentPanel (el, cfg)
30302             case 'ScrollPanel':  // ContentPanel (el, cfg)
30303                 if(cfg.autoCreate) {
30304                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
30305                 } else {
30306                     var el = this.el.createChild();
30307                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
30308                 }
30309                 
30310                 this.add(region, ret);
30311                 break;
30312             
30313             
30314             case 'TreePanel': // our new panel!
30315                 cfg.el = this.el.createChild();
30316                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
30317                 this.add(region, ret);
30318                 break;
30319             
30320             case 'NestedLayoutPanel': 
30321                 // create a new Layout (which is  a Border Layout...
30322                 var el = this.el.createChild();
30323                 var clayout = cfg.layout;
30324                 delete cfg.layout;
30325                 clayout.items   = clayout.items  || [];
30326                 // replace this exitems with the clayout ones..
30327                 xitems = clayout.items;
30328                  
30329                 
30330                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
30331                     cfg.background = false;
30332                 }
30333                 var layout = new Roo.BorderLayout(el, clayout);
30334                 
30335                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
30336                 //console.log('adding nested layout panel '  + cfg.toSource());
30337                 this.add(region, ret);
30338                 nb = {}; /// find first...
30339                 break;
30340                 
30341             case 'GridPanel': 
30342             
30343                 // needs grid and region
30344                 
30345                 //var el = this.getRegion(region).el.createChild();
30346                 var el = this.el.createChild();
30347                 // create the grid first...
30348                 
30349                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
30350                 delete cfg.grid;
30351                 if (region == 'center' && this.active ) {
30352                     cfg.background = false;
30353                 }
30354                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
30355                 
30356                 this.add(region, ret);
30357                 if (cfg.background) {
30358                     ret.on('activate', function(gp) {
30359                         if (!gp.grid.rendered) {
30360                             gp.grid.render();
30361                         }
30362                     });
30363                 } else {
30364                     grid.render();
30365                 }
30366                 break;
30367            
30368                
30369                 
30370                 
30371             default: 
30372                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
30373                 return null;
30374              // GridPanel (grid, cfg)
30375             
30376         }
30377         this.beginUpdate();
30378         // add children..
30379         var region = '';
30380         var abn = {};
30381         Roo.each(xitems, function(i)  {
30382             region = nb && i.region ? i.region : false;
30383             
30384             var add = ret.addxtype(i);
30385            
30386             if (region) {
30387                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
30388                 if (!i.background) {
30389                     abn[region] = nb[region] ;
30390                 }
30391             }
30392             
30393         });
30394         this.endUpdate();
30395
30396         // make the last non-background panel active..
30397         //if (nb) { Roo.log(abn); }
30398         if (nb) {
30399             
30400             for(var r in abn) {
30401                 region = this.getRegion(r);
30402                 if (region) {
30403                     // tried using nb[r], but it does not work..
30404                      
30405                     region.showPanel(abn[r]);
30406                    
30407                 }
30408             }
30409         }
30410         return ret;
30411         
30412     }
30413 });
30414
30415 /**
30416  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
30417  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
30418  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
30419  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
30420  * <pre><code>
30421 // shorthand
30422 var CP = Roo.ContentPanel;
30423
30424 var layout = Roo.BorderLayout.create({
30425     north: {
30426         initialSize: 25,
30427         titlebar: false,
30428         panels: [new CP("north", "North")]
30429     },
30430     west: {
30431         split:true,
30432         initialSize: 200,
30433         minSize: 175,
30434         maxSize: 400,
30435         titlebar: true,
30436         collapsible: true,
30437         panels: [new CP("west", {title: "West"})]
30438     },
30439     east: {
30440         split:true,
30441         initialSize: 202,
30442         minSize: 175,
30443         maxSize: 400,
30444         titlebar: true,
30445         collapsible: true,
30446         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
30447     },
30448     south: {
30449         split:true,
30450         initialSize: 100,
30451         minSize: 100,
30452         maxSize: 200,
30453         titlebar: true,
30454         collapsible: true,
30455         panels: [new CP("south", {title: "South", closable: true})]
30456     },
30457     center: {
30458         titlebar: true,
30459         autoScroll:true,
30460         resizeTabs: true,
30461         minTabWidth: 50,
30462         preferredTabWidth: 150,
30463         panels: [
30464             new CP("center1", {title: "Close Me", closable: true}),
30465             new CP("center2", {title: "Center Panel", closable: false})
30466         ]
30467     }
30468 }, document.body);
30469
30470 layout.getRegion("center").showPanel("center1");
30471 </code></pre>
30472  * @param config
30473  * @param targetEl
30474  */
30475 Roo.BorderLayout.create = function(config, targetEl){
30476     var layout = new Roo.BorderLayout(targetEl || document.body, config);
30477     layout.beginUpdate();
30478     var regions = Roo.BorderLayout.RegionFactory.validRegions;
30479     for(var j = 0, jlen = regions.length; j < jlen; j++){
30480         var lr = regions[j];
30481         if(layout.regions[lr] && config[lr].panels){
30482             var r = layout.regions[lr];
30483             var ps = config[lr].panels;
30484             layout.addTypedPanels(r, ps);
30485         }
30486     }
30487     layout.endUpdate();
30488     return layout;
30489 };
30490
30491 // private
30492 Roo.BorderLayout.RegionFactory = {
30493     // private
30494     validRegions : ["north","south","east","west","center"],
30495
30496     // private
30497     create : function(target, mgr, config){
30498         target = target.toLowerCase();
30499         if(config.lightweight || config.basic){
30500             return new Roo.BasicLayoutRegion(mgr, config, target);
30501         }
30502         switch(target){
30503             case "north":
30504                 return new Roo.NorthLayoutRegion(mgr, config);
30505             case "south":
30506                 return new Roo.SouthLayoutRegion(mgr, config);
30507             case "east":
30508                 return new Roo.EastLayoutRegion(mgr, config);
30509             case "west":
30510                 return new Roo.WestLayoutRegion(mgr, config);
30511             case "center":
30512                 return new Roo.CenterLayoutRegion(mgr, config);
30513         }
30514         throw 'Layout region "'+target+'" not supported.';
30515     }
30516 };/*
30517  * Based on:
30518  * Ext JS Library 1.1.1
30519  * Copyright(c) 2006-2007, Ext JS, LLC.
30520  *
30521  * Originally Released Under LGPL - original licence link has changed is not relivant.
30522  *
30523  * Fork - LGPL
30524  * <script type="text/javascript">
30525  */
30526  
30527 /**
30528  * @class Roo.BasicLayoutRegion
30529  * @extends Roo.util.Observable
30530  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
30531  * and does not have a titlebar, tabs or any other features. All it does is size and position 
30532  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
30533  */
30534 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
30535     this.mgr = mgr;
30536     this.position  = pos;
30537     this.events = {
30538         /**
30539          * @scope Roo.BasicLayoutRegion
30540          */
30541         
30542         /**
30543          * @event beforeremove
30544          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
30545          * @param {Roo.LayoutRegion} this
30546          * @param {Roo.ContentPanel} panel The panel
30547          * @param {Object} e The cancel event object
30548          */
30549         "beforeremove" : true,
30550         /**
30551          * @event invalidated
30552          * Fires when the layout for this region is changed.
30553          * @param {Roo.LayoutRegion} this
30554          */
30555         "invalidated" : true,
30556         /**
30557          * @event visibilitychange
30558          * Fires when this region is shown or hidden 
30559          * @param {Roo.LayoutRegion} this
30560          * @param {Boolean} visibility true or false
30561          */
30562         "visibilitychange" : true,
30563         /**
30564          * @event paneladded
30565          * Fires when a panel is added. 
30566          * @param {Roo.LayoutRegion} this
30567          * @param {Roo.ContentPanel} panel The panel
30568          */
30569         "paneladded" : true,
30570         /**
30571          * @event panelremoved
30572          * Fires when a panel is removed. 
30573          * @param {Roo.LayoutRegion} this
30574          * @param {Roo.ContentPanel} panel The panel
30575          */
30576         "panelremoved" : true,
30577         /**
30578          * @event collapsed
30579          * Fires when this region is collapsed.
30580          * @param {Roo.LayoutRegion} this
30581          */
30582         "collapsed" : true,
30583         /**
30584          * @event expanded
30585          * Fires when this region is expanded.
30586          * @param {Roo.LayoutRegion} this
30587          */
30588         "expanded" : true,
30589         /**
30590          * @event slideshow
30591          * Fires when this region is slid into view.
30592          * @param {Roo.LayoutRegion} this
30593          */
30594         "slideshow" : true,
30595         /**
30596          * @event slidehide
30597          * Fires when this region slides out of view. 
30598          * @param {Roo.LayoutRegion} this
30599          */
30600         "slidehide" : true,
30601         /**
30602          * @event panelactivated
30603          * Fires when a panel is activated. 
30604          * @param {Roo.LayoutRegion} this
30605          * @param {Roo.ContentPanel} panel The activated panel
30606          */
30607         "panelactivated" : true,
30608         /**
30609          * @event resized
30610          * Fires when the user resizes this region. 
30611          * @param {Roo.LayoutRegion} this
30612          * @param {Number} newSize The new size (width for east/west, height for north/south)
30613          */
30614         "resized" : true
30615     };
30616     /** A collection of panels in this region. @type Roo.util.MixedCollection */
30617     this.panels = new Roo.util.MixedCollection();
30618     this.panels.getKey = this.getPanelId.createDelegate(this);
30619     this.box = null;
30620     this.activePanel = null;
30621     // ensure listeners are added...
30622     
30623     if (config.listeners || config.events) {
30624         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
30625             listeners : config.listeners || {},
30626             events : config.events || {}
30627         });
30628     }
30629     
30630     if(skipConfig !== true){
30631         this.applyConfig(config);
30632     }
30633 };
30634
30635 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
30636     getPanelId : function(p){
30637         return p.getId();
30638     },
30639     
30640     applyConfig : function(config){
30641         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30642         this.config = config;
30643         
30644     },
30645     
30646     /**
30647      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
30648      * the width, for horizontal (north, south) the height.
30649      * @param {Number} newSize The new width or height
30650      */
30651     resizeTo : function(newSize){
30652         var el = this.el ? this.el :
30653                  (this.activePanel ? this.activePanel.getEl() : null);
30654         if(el){
30655             switch(this.position){
30656                 case "east":
30657                 case "west":
30658                     el.setWidth(newSize);
30659                     this.fireEvent("resized", this, newSize);
30660                 break;
30661                 case "north":
30662                 case "south":
30663                     el.setHeight(newSize);
30664                     this.fireEvent("resized", this, newSize);
30665                 break;                
30666             }
30667         }
30668     },
30669     
30670     getBox : function(){
30671         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
30672     },
30673     
30674     getMargins : function(){
30675         return this.margins;
30676     },
30677     
30678     updateBox : function(box){
30679         this.box = box;
30680         var el = this.activePanel.getEl();
30681         el.dom.style.left = box.x + "px";
30682         el.dom.style.top = box.y + "px";
30683         this.activePanel.setSize(box.width, box.height);
30684     },
30685     
30686     /**
30687      * Returns the container element for this region.
30688      * @return {Roo.Element}
30689      */
30690     getEl : function(){
30691         return this.activePanel;
30692     },
30693     
30694     /**
30695      * Returns true if this region is currently visible.
30696      * @return {Boolean}
30697      */
30698     isVisible : function(){
30699         return this.activePanel ? true : false;
30700     },
30701     
30702     setActivePanel : function(panel){
30703         panel = this.getPanel(panel);
30704         if(this.activePanel && this.activePanel != panel){
30705             this.activePanel.setActiveState(false);
30706             this.activePanel.getEl().setLeftTop(-10000,-10000);
30707         }
30708         this.activePanel = panel;
30709         panel.setActiveState(true);
30710         if(this.box){
30711             panel.setSize(this.box.width, this.box.height);
30712         }
30713         this.fireEvent("panelactivated", this, panel);
30714         this.fireEvent("invalidated");
30715     },
30716     
30717     /**
30718      * Show the specified panel.
30719      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
30720      * @return {Roo.ContentPanel} The shown panel or null
30721      */
30722     showPanel : function(panel){
30723         if(panel = this.getPanel(panel)){
30724             this.setActivePanel(panel);
30725         }
30726         return panel;
30727     },
30728     
30729     /**
30730      * Get the active panel for this region.
30731      * @return {Roo.ContentPanel} The active panel or null
30732      */
30733     getActivePanel : function(){
30734         return this.activePanel;
30735     },
30736     
30737     /**
30738      * Add the passed ContentPanel(s)
30739      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30740      * @return {Roo.ContentPanel} The panel added (if only one was added)
30741      */
30742     add : function(panel){
30743         if(arguments.length > 1){
30744             for(var i = 0, len = arguments.length; i < len; i++) {
30745                 this.add(arguments[i]);
30746             }
30747             return null;
30748         }
30749         if(this.hasPanel(panel)){
30750             this.showPanel(panel);
30751             return panel;
30752         }
30753         var el = panel.getEl();
30754         if(el.dom.parentNode != this.mgr.el.dom){
30755             this.mgr.el.dom.appendChild(el.dom);
30756         }
30757         if(panel.setRegion){
30758             panel.setRegion(this);
30759         }
30760         this.panels.add(panel);
30761         el.setStyle("position", "absolute");
30762         if(!panel.background){
30763             this.setActivePanel(panel);
30764             if(this.config.initialSize && this.panels.getCount()==1){
30765                 this.resizeTo(this.config.initialSize);
30766             }
30767         }
30768         this.fireEvent("paneladded", this, panel);
30769         return panel;
30770     },
30771     
30772     /**
30773      * Returns true if the panel is in this region.
30774      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30775      * @return {Boolean}
30776      */
30777     hasPanel : function(panel){
30778         if(typeof panel == "object"){ // must be panel obj
30779             panel = panel.getId();
30780         }
30781         return this.getPanel(panel) ? true : false;
30782     },
30783     
30784     /**
30785      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30786      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30787      * @param {Boolean} preservePanel Overrides the config preservePanel option
30788      * @return {Roo.ContentPanel} The panel that was removed
30789      */
30790     remove : function(panel, preservePanel){
30791         panel = this.getPanel(panel);
30792         if(!panel){
30793             return null;
30794         }
30795         var e = {};
30796         this.fireEvent("beforeremove", this, panel, e);
30797         if(e.cancel === true){
30798             return null;
30799         }
30800         var panelId = panel.getId();
30801         this.panels.removeKey(panelId);
30802         return panel;
30803     },
30804     
30805     /**
30806      * Returns the panel specified or null if it's not in this region.
30807      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30808      * @return {Roo.ContentPanel}
30809      */
30810     getPanel : function(id){
30811         if(typeof id == "object"){ // must be panel obj
30812             return id;
30813         }
30814         return this.panels.get(id);
30815     },
30816     
30817     /**
30818      * Returns this regions position (north/south/east/west/center).
30819      * @return {String} 
30820      */
30821     getPosition: function(){
30822         return this.position;    
30823     }
30824 });/*
30825  * Based on:
30826  * Ext JS Library 1.1.1
30827  * Copyright(c) 2006-2007, Ext JS, LLC.
30828  *
30829  * Originally Released Under LGPL - original licence link has changed is not relivant.
30830  *
30831  * Fork - LGPL
30832  * <script type="text/javascript">
30833  */
30834  
30835 /**
30836  * @class Roo.LayoutRegion
30837  * @extends Roo.BasicLayoutRegion
30838  * This class represents a region in a layout manager.
30839  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
30840  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
30841  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
30842  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
30843  * @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})
30844  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
30845  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
30846  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
30847  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
30848  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
30849  * @cfg {String}    title           The title for the region (overrides panel titles)
30850  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
30851  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
30852  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
30853  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
30854  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
30855  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
30856  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
30857  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
30858  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
30859  * @cfg {Boolean}   showPin         True to show a pin button
30860  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
30861  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
30862  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
30863  * @cfg {Number}    width           For East/West panels
30864  * @cfg {Number}    height          For North/South panels
30865  * @cfg {Boolean}   split           To show the splitter
30866  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
30867  */
30868 Roo.LayoutRegion = function(mgr, config, pos){
30869     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
30870     var dh = Roo.DomHelper;
30871     /** This region's container element 
30872     * @type Roo.Element */
30873     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
30874     /** This region's title element 
30875     * @type Roo.Element */
30876
30877     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
30878         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
30879         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
30880     ]}, true);
30881     this.titleEl.enableDisplayMode();
30882     /** This region's title text element 
30883     * @type HTMLElement */
30884     this.titleTextEl = this.titleEl.dom.firstChild;
30885     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
30886     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
30887     this.closeBtn.enableDisplayMode();
30888     this.closeBtn.on("click", this.closeClicked, this);
30889     this.closeBtn.hide();
30890
30891     this.createBody(config);
30892     this.visible = true;
30893     this.collapsed = false;
30894
30895     if(config.hideWhenEmpty){
30896         this.hide();
30897         this.on("paneladded", this.validateVisibility, this);
30898         this.on("panelremoved", this.validateVisibility, this);
30899     }
30900     this.applyConfig(config);
30901 };
30902
30903 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
30904
30905     createBody : function(){
30906         /** This region's body element 
30907         * @type Roo.Element */
30908         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
30909     },
30910
30911     applyConfig : function(c){
30912         if(c.collapsible && this.position != "center" && !this.collapsedEl){
30913             var dh = Roo.DomHelper;
30914             if(c.titlebar !== false){
30915                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
30916                 this.collapseBtn.on("click", this.collapse, this);
30917                 this.collapseBtn.enableDisplayMode();
30918
30919                 if(c.showPin === true || this.showPin){
30920                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
30921                     this.stickBtn.enableDisplayMode();
30922                     this.stickBtn.on("click", this.expand, this);
30923                     this.stickBtn.hide();
30924                 }
30925             }
30926             /** This region's collapsed element
30927             * @type Roo.Element */
30928             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
30929                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
30930             ]}, true);
30931             if(c.floatable !== false){
30932                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
30933                this.collapsedEl.on("click", this.collapseClick, this);
30934             }
30935
30936             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
30937                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
30938                    id: "message", unselectable: "on", style:{"float":"left"}});
30939                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
30940              }
30941             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
30942             this.expandBtn.on("click", this.expand, this);
30943         }
30944         if(this.collapseBtn){
30945             this.collapseBtn.setVisible(c.collapsible == true);
30946         }
30947         this.cmargins = c.cmargins || this.cmargins ||
30948                          (this.position == "west" || this.position == "east" ?
30949                              {top: 0, left: 2, right:2, bottom: 0} :
30950                              {top: 2, left: 0, right:0, bottom: 2});
30951         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30952         this.bottomTabs = c.tabPosition != "top";
30953         this.autoScroll = c.autoScroll || false;
30954         if(this.autoScroll){
30955             this.bodyEl.setStyle("overflow", "auto");
30956         }else{
30957             this.bodyEl.setStyle("overflow", "hidden");
30958         }
30959         //if(c.titlebar !== false){
30960             if((!c.titlebar && !c.title) || c.titlebar === false){
30961                 this.titleEl.hide();
30962             }else{
30963                 this.titleEl.show();
30964                 if(c.title){
30965                     this.titleTextEl.innerHTML = c.title;
30966                 }
30967             }
30968         //}
30969         this.duration = c.duration || .30;
30970         this.slideDuration = c.slideDuration || .45;
30971         this.config = c;
30972         if(c.collapsed){
30973             this.collapse(true);
30974         }
30975         if(c.hidden){
30976             this.hide();
30977         }
30978     },
30979     /**
30980      * Returns true if this region is currently visible.
30981      * @return {Boolean}
30982      */
30983     isVisible : function(){
30984         return this.visible;
30985     },
30986
30987     /**
30988      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
30989      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
30990      */
30991     setCollapsedTitle : function(title){
30992         title = title || "&#160;";
30993         if(this.collapsedTitleTextEl){
30994             this.collapsedTitleTextEl.innerHTML = title;
30995         }
30996     },
30997
30998     getBox : function(){
30999         var b;
31000         if(!this.collapsed){
31001             b = this.el.getBox(false, true);
31002         }else{
31003             b = this.collapsedEl.getBox(false, true);
31004         }
31005         return b;
31006     },
31007
31008     getMargins : function(){
31009         return this.collapsed ? this.cmargins : this.margins;
31010     },
31011
31012     highlight : function(){
31013         this.el.addClass("x-layout-panel-dragover");
31014     },
31015
31016     unhighlight : function(){
31017         this.el.removeClass("x-layout-panel-dragover");
31018     },
31019
31020     updateBox : function(box){
31021         this.box = box;
31022         if(!this.collapsed){
31023             this.el.dom.style.left = box.x + "px";
31024             this.el.dom.style.top = box.y + "px";
31025             this.updateBody(box.width, box.height);
31026         }else{
31027             this.collapsedEl.dom.style.left = box.x + "px";
31028             this.collapsedEl.dom.style.top = box.y + "px";
31029             this.collapsedEl.setSize(box.width, box.height);
31030         }
31031         if(this.tabs){
31032             this.tabs.autoSizeTabs();
31033         }
31034     },
31035
31036     updateBody : function(w, h){
31037         if(w !== null){
31038             this.el.setWidth(w);
31039             w -= this.el.getBorderWidth("rl");
31040             if(this.config.adjustments){
31041                 w += this.config.adjustments[0];
31042             }
31043         }
31044         if(h !== null){
31045             this.el.setHeight(h);
31046             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
31047             h -= this.el.getBorderWidth("tb");
31048             if(this.config.adjustments){
31049                 h += this.config.adjustments[1];
31050             }
31051             this.bodyEl.setHeight(h);
31052             if(this.tabs){
31053                 h = this.tabs.syncHeight(h);
31054             }
31055         }
31056         if(this.panelSize){
31057             w = w !== null ? w : this.panelSize.width;
31058             h = h !== null ? h : this.panelSize.height;
31059         }
31060         if(this.activePanel){
31061             var el = this.activePanel.getEl();
31062             w = w !== null ? w : el.getWidth();
31063             h = h !== null ? h : el.getHeight();
31064             this.panelSize = {width: w, height: h};
31065             this.activePanel.setSize(w, h);
31066         }
31067         if(Roo.isIE && this.tabs){
31068             this.tabs.el.repaint();
31069         }
31070     },
31071
31072     /**
31073      * Returns the container element for this region.
31074      * @return {Roo.Element}
31075      */
31076     getEl : function(){
31077         return this.el;
31078     },
31079
31080     /**
31081      * Hides this region.
31082      */
31083     hide : function(){
31084         if(!this.collapsed){
31085             this.el.dom.style.left = "-2000px";
31086             this.el.hide();
31087         }else{
31088             this.collapsedEl.dom.style.left = "-2000px";
31089             this.collapsedEl.hide();
31090         }
31091         this.visible = false;
31092         this.fireEvent("visibilitychange", this, false);
31093     },
31094
31095     /**
31096      * Shows this region if it was previously hidden.
31097      */
31098     show : function(){
31099         if(!this.collapsed){
31100             this.el.show();
31101         }else{
31102             this.collapsedEl.show();
31103         }
31104         this.visible = true;
31105         this.fireEvent("visibilitychange", this, true);
31106     },
31107
31108     closeClicked : function(){
31109         if(this.activePanel){
31110             this.remove(this.activePanel);
31111         }
31112     },
31113
31114     collapseClick : function(e){
31115         if(this.isSlid){
31116            e.stopPropagation();
31117            this.slideIn();
31118         }else{
31119            e.stopPropagation();
31120            this.slideOut();
31121         }
31122     },
31123
31124     /**
31125      * Collapses this region.
31126      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
31127      */
31128     collapse : function(skipAnim){
31129         if(this.collapsed) return;
31130         this.collapsed = true;
31131         if(this.split){
31132             this.split.el.hide();
31133         }
31134         if(this.config.animate && skipAnim !== true){
31135             this.fireEvent("invalidated", this);
31136             this.animateCollapse();
31137         }else{
31138             this.el.setLocation(-20000,-20000);
31139             this.el.hide();
31140             this.collapsedEl.show();
31141             this.fireEvent("collapsed", this);
31142             this.fireEvent("invalidated", this);
31143         }
31144     },
31145
31146     animateCollapse : function(){
31147         // overridden
31148     },
31149
31150     /**
31151      * Expands this region if it was previously collapsed.
31152      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
31153      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
31154      */
31155     expand : function(e, skipAnim){
31156         if(e) e.stopPropagation();
31157         if(!this.collapsed || this.el.hasActiveFx()) return;
31158         if(this.isSlid){
31159             this.afterSlideIn();
31160             skipAnim = true;
31161         }
31162         this.collapsed = false;
31163         if(this.config.animate && skipAnim !== true){
31164             this.animateExpand();
31165         }else{
31166             this.el.show();
31167             if(this.split){
31168                 this.split.el.show();
31169             }
31170             this.collapsedEl.setLocation(-2000,-2000);
31171             this.collapsedEl.hide();
31172             this.fireEvent("invalidated", this);
31173             this.fireEvent("expanded", this);
31174         }
31175     },
31176
31177     animateExpand : function(){
31178         // overridden
31179     },
31180
31181     initTabs : function()
31182     {
31183         this.bodyEl.setStyle("overflow", "hidden");
31184         var ts = new Roo.TabPanel(
31185                 this.bodyEl.dom,
31186                 {
31187                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
31188                     disableTooltips: this.config.disableTabTips,
31189                     toolbar : this.config.toolbar
31190                 }
31191         );
31192         if(this.config.hideTabs){
31193             ts.stripWrap.setDisplayed(false);
31194         }
31195         this.tabs = ts;
31196         ts.resizeTabs = this.config.resizeTabs === true;
31197         ts.minTabWidth = this.config.minTabWidth || 40;
31198         ts.maxTabWidth = this.config.maxTabWidth || 250;
31199         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
31200         ts.monitorResize = false;
31201         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
31202         ts.bodyEl.addClass('x-layout-tabs-body');
31203         this.panels.each(this.initPanelAsTab, this);
31204     },
31205
31206     initPanelAsTab : function(panel){
31207         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
31208                     this.config.closeOnTab && panel.isClosable());
31209         if(panel.tabTip !== undefined){
31210             ti.setTooltip(panel.tabTip);
31211         }
31212         ti.on("activate", function(){
31213               this.setActivePanel(panel);
31214         }, this);
31215         if(this.config.closeOnTab){
31216             ti.on("beforeclose", function(t, e){
31217                 e.cancel = true;
31218                 this.remove(panel);
31219             }, this);
31220         }
31221         return ti;
31222     },
31223
31224     updatePanelTitle : function(panel, title){
31225         if(this.activePanel == panel){
31226             this.updateTitle(title);
31227         }
31228         if(this.tabs){
31229             var ti = this.tabs.getTab(panel.getEl().id);
31230             ti.setText(title);
31231             if(panel.tabTip !== undefined){
31232                 ti.setTooltip(panel.tabTip);
31233             }
31234         }
31235     },
31236
31237     updateTitle : function(title){
31238         if(this.titleTextEl && !this.config.title){
31239             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
31240         }
31241     },
31242
31243     setActivePanel : function(panel){
31244         panel = this.getPanel(panel);
31245         if(this.activePanel && this.activePanel != panel){
31246             this.activePanel.setActiveState(false);
31247         }
31248         this.activePanel = panel;
31249         panel.setActiveState(true);
31250         if(this.panelSize){
31251             panel.setSize(this.panelSize.width, this.panelSize.height);
31252         }
31253         if(this.closeBtn){
31254             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
31255         }
31256         this.updateTitle(panel.getTitle());
31257         if(this.tabs){
31258             this.fireEvent("invalidated", this);
31259         }
31260         this.fireEvent("panelactivated", this, panel);
31261     },
31262
31263     /**
31264      * Shows the specified panel.
31265      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
31266      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
31267      */
31268     showPanel : function(panel){
31269         if(panel = this.getPanel(panel)){
31270             if(this.tabs){
31271                 var tab = this.tabs.getTab(panel.getEl().id);
31272                 if(tab.isHidden()){
31273                     this.tabs.unhideTab(tab.id);
31274                 }
31275                 tab.activate();
31276             }else{
31277                 this.setActivePanel(panel);
31278             }
31279         }
31280         return panel;
31281     },
31282
31283     /**
31284      * Get the active panel for this region.
31285      * @return {Roo.ContentPanel} The active panel or null
31286      */
31287     getActivePanel : function(){
31288         return this.activePanel;
31289     },
31290
31291     validateVisibility : function(){
31292         if(this.panels.getCount() < 1){
31293             this.updateTitle("&#160;");
31294             this.closeBtn.hide();
31295             this.hide();
31296         }else{
31297             if(!this.isVisible()){
31298                 this.show();
31299             }
31300         }
31301     },
31302
31303     /**
31304      * Adds the passed ContentPanel(s) to this region.
31305      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
31306      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
31307      */
31308     add : function(panel){
31309         if(arguments.length > 1){
31310             for(var i = 0, len = arguments.length; i < len; i++) {
31311                 this.add(arguments[i]);
31312             }
31313             return null;
31314         }
31315         if(this.hasPanel(panel)){
31316             this.showPanel(panel);
31317             return panel;
31318         }
31319         panel.setRegion(this);
31320         this.panels.add(panel);
31321         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
31322             this.bodyEl.dom.appendChild(panel.getEl().dom);
31323             if(panel.background !== true){
31324                 this.setActivePanel(panel);
31325             }
31326             this.fireEvent("paneladded", this, panel);
31327             return panel;
31328         }
31329         if(!this.tabs){
31330             this.initTabs();
31331         }else{
31332             this.initPanelAsTab(panel);
31333         }
31334         if(panel.background !== true){
31335             this.tabs.activate(panel.getEl().id);
31336         }
31337         this.fireEvent("paneladded", this, panel);
31338         return panel;
31339     },
31340
31341     /**
31342      * Hides the tab for the specified panel.
31343      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
31344      */
31345     hidePanel : function(panel){
31346         if(this.tabs && (panel = this.getPanel(panel))){
31347             this.tabs.hideTab(panel.getEl().id);
31348         }
31349     },
31350
31351     /**
31352      * Unhides the tab for a previously hidden panel.
31353      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
31354      */
31355     unhidePanel : function(panel){
31356         if(this.tabs && (panel = this.getPanel(panel))){
31357             this.tabs.unhideTab(panel.getEl().id);
31358         }
31359     },
31360
31361     clearPanels : function(){
31362         while(this.panels.getCount() > 0){
31363              this.remove(this.panels.first());
31364         }
31365     },
31366
31367     /**
31368      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
31369      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
31370      * @param {Boolean} preservePanel Overrides the config preservePanel option
31371      * @return {Roo.ContentPanel} The panel that was removed
31372      */
31373     remove : function(panel, preservePanel){
31374         panel = this.getPanel(panel);
31375         if(!panel){
31376             return null;
31377         }
31378         var e = {};
31379         this.fireEvent("beforeremove", this, panel, e);
31380         if(e.cancel === true){
31381             return null;
31382         }
31383         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
31384         var panelId = panel.getId();
31385         this.panels.removeKey(panelId);
31386         if(preservePanel){
31387             document.body.appendChild(panel.getEl().dom);
31388         }
31389         if(this.tabs){
31390             this.tabs.removeTab(panel.getEl().id);
31391         }else if (!preservePanel){
31392             this.bodyEl.dom.removeChild(panel.getEl().dom);
31393         }
31394         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
31395             var p = this.panels.first();
31396             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
31397             tempEl.appendChild(p.getEl().dom);
31398             this.bodyEl.update("");
31399             this.bodyEl.dom.appendChild(p.getEl().dom);
31400             tempEl = null;
31401             this.updateTitle(p.getTitle());
31402             this.tabs = null;
31403             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
31404             this.setActivePanel(p);
31405         }
31406         panel.setRegion(null);
31407         if(this.activePanel == panel){
31408             this.activePanel = null;
31409         }
31410         if(this.config.autoDestroy !== false && preservePanel !== true){
31411             try{panel.destroy();}catch(e){}
31412         }
31413         this.fireEvent("panelremoved", this, panel);
31414         return panel;
31415     },
31416
31417     /**
31418      * Returns the TabPanel component used by this region
31419      * @return {Roo.TabPanel}
31420      */
31421     getTabs : function(){
31422         return this.tabs;
31423     },
31424
31425     createTool : function(parentEl, className){
31426         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
31427             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
31428         btn.addClassOnOver("x-layout-tools-button-over");
31429         return btn;
31430     }
31431 });/*
31432  * Based on:
31433  * Ext JS Library 1.1.1
31434  * Copyright(c) 2006-2007, Ext JS, LLC.
31435  *
31436  * Originally Released Under LGPL - original licence link has changed is not relivant.
31437  *
31438  * Fork - LGPL
31439  * <script type="text/javascript">
31440  */
31441  
31442
31443
31444 /**
31445  * @class Roo.SplitLayoutRegion
31446  * @extends Roo.LayoutRegion
31447  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
31448  */
31449 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
31450     this.cursor = cursor;
31451     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
31452 };
31453
31454 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
31455     splitTip : "Drag to resize.",
31456     collapsibleSplitTip : "Drag to resize. Double click to hide.",
31457     useSplitTips : false,
31458
31459     applyConfig : function(config){
31460         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
31461         if(config.split){
31462             if(!this.split){
31463                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
31464                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
31465                 /** The SplitBar for this region 
31466                 * @type Roo.SplitBar */
31467                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
31468                 this.split.on("moved", this.onSplitMove, this);
31469                 this.split.useShim = config.useShim === true;
31470                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
31471                 if(this.useSplitTips){
31472                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
31473                 }
31474                 if(config.collapsible){
31475                     this.split.el.on("dblclick", this.collapse,  this);
31476                 }
31477             }
31478             if(typeof config.minSize != "undefined"){
31479                 this.split.minSize = config.minSize;
31480             }
31481             if(typeof config.maxSize != "undefined"){
31482                 this.split.maxSize = config.maxSize;
31483             }
31484             if(config.hideWhenEmpty || config.hidden || config.collapsed){
31485                 this.hideSplitter();
31486             }
31487         }
31488     },
31489
31490     getHMaxSize : function(){
31491          var cmax = this.config.maxSize || 10000;
31492          var center = this.mgr.getRegion("center");
31493          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
31494     },
31495
31496     getVMaxSize : function(){
31497          var cmax = this.config.maxSize || 10000;
31498          var center = this.mgr.getRegion("center");
31499          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
31500     },
31501
31502     onSplitMove : function(split, newSize){
31503         this.fireEvent("resized", this, newSize);
31504     },
31505     
31506     /** 
31507      * Returns the {@link Roo.SplitBar} for this region.
31508      * @return {Roo.SplitBar}
31509      */
31510     getSplitBar : function(){
31511         return this.split;
31512     },
31513     
31514     hide : function(){
31515         this.hideSplitter();
31516         Roo.SplitLayoutRegion.superclass.hide.call(this);
31517     },
31518
31519     hideSplitter : function(){
31520         if(this.split){
31521             this.split.el.setLocation(-2000,-2000);
31522             this.split.el.hide();
31523         }
31524     },
31525
31526     show : function(){
31527         if(this.split){
31528             this.split.el.show();
31529         }
31530         Roo.SplitLayoutRegion.superclass.show.call(this);
31531     },
31532     
31533     beforeSlide: function(){
31534         if(Roo.isGecko){// firefox overflow auto bug workaround
31535             this.bodyEl.clip();
31536             if(this.tabs) this.tabs.bodyEl.clip();
31537             if(this.activePanel){
31538                 this.activePanel.getEl().clip();
31539                 
31540                 if(this.activePanel.beforeSlide){
31541                     this.activePanel.beforeSlide();
31542                 }
31543             }
31544         }
31545     },
31546     
31547     afterSlide : function(){
31548         if(Roo.isGecko){// firefox overflow auto bug workaround
31549             this.bodyEl.unclip();
31550             if(this.tabs) this.tabs.bodyEl.unclip();
31551             if(this.activePanel){
31552                 this.activePanel.getEl().unclip();
31553                 if(this.activePanel.afterSlide){
31554                     this.activePanel.afterSlide();
31555                 }
31556             }
31557         }
31558     },
31559
31560     initAutoHide : function(){
31561         if(this.autoHide !== false){
31562             if(!this.autoHideHd){
31563                 var st = new Roo.util.DelayedTask(this.slideIn, this);
31564                 this.autoHideHd = {
31565                     "mouseout": function(e){
31566                         if(!e.within(this.el, true)){
31567                             st.delay(500);
31568                         }
31569                     },
31570                     "mouseover" : function(e){
31571                         st.cancel();
31572                     },
31573                     scope : this
31574                 };
31575             }
31576             this.el.on(this.autoHideHd);
31577         }
31578     },
31579
31580     clearAutoHide : function(){
31581         if(this.autoHide !== false){
31582             this.el.un("mouseout", this.autoHideHd.mouseout);
31583             this.el.un("mouseover", this.autoHideHd.mouseover);
31584         }
31585     },
31586
31587     clearMonitor : function(){
31588         Roo.get(document).un("click", this.slideInIf, this);
31589     },
31590
31591     // these names are backwards but not changed for compat
31592     slideOut : function(){
31593         if(this.isSlid || this.el.hasActiveFx()){
31594             return;
31595         }
31596         this.isSlid = true;
31597         if(this.collapseBtn){
31598             this.collapseBtn.hide();
31599         }
31600         this.closeBtnState = this.closeBtn.getStyle('display');
31601         this.closeBtn.hide();
31602         if(this.stickBtn){
31603             this.stickBtn.show();
31604         }
31605         this.el.show();
31606         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
31607         this.beforeSlide();
31608         this.el.setStyle("z-index", 10001);
31609         this.el.slideIn(this.getSlideAnchor(), {
31610             callback: function(){
31611                 this.afterSlide();
31612                 this.initAutoHide();
31613                 Roo.get(document).on("click", this.slideInIf, this);
31614                 this.fireEvent("slideshow", this);
31615             },
31616             scope: this,
31617             block: true
31618         });
31619     },
31620
31621     afterSlideIn : function(){
31622         this.clearAutoHide();
31623         this.isSlid = false;
31624         this.clearMonitor();
31625         this.el.setStyle("z-index", "");
31626         if(this.collapseBtn){
31627             this.collapseBtn.show();
31628         }
31629         this.closeBtn.setStyle('display', this.closeBtnState);
31630         if(this.stickBtn){
31631             this.stickBtn.hide();
31632         }
31633         this.fireEvent("slidehide", this);
31634     },
31635
31636     slideIn : function(cb){
31637         if(!this.isSlid || this.el.hasActiveFx()){
31638             Roo.callback(cb);
31639             return;
31640         }
31641         this.isSlid = false;
31642         this.beforeSlide();
31643         this.el.slideOut(this.getSlideAnchor(), {
31644             callback: function(){
31645                 this.el.setLeftTop(-10000, -10000);
31646                 this.afterSlide();
31647                 this.afterSlideIn();
31648                 Roo.callback(cb);
31649             },
31650             scope: this,
31651             block: true
31652         });
31653     },
31654     
31655     slideInIf : function(e){
31656         if(!e.within(this.el)){
31657             this.slideIn();
31658         }
31659     },
31660
31661     animateCollapse : function(){
31662         this.beforeSlide();
31663         this.el.setStyle("z-index", 20000);
31664         var anchor = this.getSlideAnchor();
31665         this.el.slideOut(anchor, {
31666             callback : function(){
31667                 this.el.setStyle("z-index", "");
31668                 this.collapsedEl.slideIn(anchor, {duration:.3});
31669                 this.afterSlide();
31670                 this.el.setLocation(-10000,-10000);
31671                 this.el.hide();
31672                 this.fireEvent("collapsed", this);
31673             },
31674             scope: this,
31675             block: true
31676         });
31677     },
31678
31679     animateExpand : function(){
31680         this.beforeSlide();
31681         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
31682         this.el.setStyle("z-index", 20000);
31683         this.collapsedEl.hide({
31684             duration:.1
31685         });
31686         this.el.slideIn(this.getSlideAnchor(), {
31687             callback : function(){
31688                 this.el.setStyle("z-index", "");
31689                 this.afterSlide();
31690                 if(this.split){
31691                     this.split.el.show();
31692                 }
31693                 this.fireEvent("invalidated", this);
31694                 this.fireEvent("expanded", this);
31695             },
31696             scope: this,
31697             block: true
31698         });
31699     },
31700
31701     anchors : {
31702         "west" : "left",
31703         "east" : "right",
31704         "north" : "top",
31705         "south" : "bottom"
31706     },
31707
31708     sanchors : {
31709         "west" : "l",
31710         "east" : "r",
31711         "north" : "t",
31712         "south" : "b"
31713     },
31714
31715     canchors : {
31716         "west" : "tl-tr",
31717         "east" : "tr-tl",
31718         "north" : "tl-bl",
31719         "south" : "bl-tl"
31720     },
31721
31722     getAnchor : function(){
31723         return this.anchors[this.position];
31724     },
31725
31726     getCollapseAnchor : function(){
31727         return this.canchors[this.position];
31728     },
31729
31730     getSlideAnchor : function(){
31731         return this.sanchors[this.position];
31732     },
31733
31734     getAlignAdj : function(){
31735         var cm = this.cmargins;
31736         switch(this.position){
31737             case "west":
31738                 return [0, 0];
31739             break;
31740             case "east":
31741                 return [0, 0];
31742             break;
31743             case "north":
31744                 return [0, 0];
31745             break;
31746             case "south":
31747                 return [0, 0];
31748             break;
31749         }
31750     },
31751
31752     getExpandAdj : function(){
31753         var c = this.collapsedEl, cm = this.cmargins;
31754         switch(this.position){
31755             case "west":
31756                 return [-(cm.right+c.getWidth()+cm.left), 0];
31757             break;
31758             case "east":
31759                 return [cm.right+c.getWidth()+cm.left, 0];
31760             break;
31761             case "north":
31762                 return [0, -(cm.top+cm.bottom+c.getHeight())];
31763             break;
31764             case "south":
31765                 return [0, cm.top+cm.bottom+c.getHeight()];
31766             break;
31767         }
31768     }
31769 });/*
31770  * Based on:
31771  * Ext JS Library 1.1.1
31772  * Copyright(c) 2006-2007, Ext JS, LLC.
31773  *
31774  * Originally Released Under LGPL - original licence link has changed is not relivant.
31775  *
31776  * Fork - LGPL
31777  * <script type="text/javascript">
31778  */
31779 /*
31780  * These classes are private internal classes
31781  */
31782 Roo.CenterLayoutRegion = function(mgr, config){
31783     Roo.LayoutRegion.call(this, mgr, config, "center");
31784     this.visible = true;
31785     this.minWidth = config.minWidth || 20;
31786     this.minHeight = config.minHeight || 20;
31787 };
31788
31789 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
31790     hide : function(){
31791         // center panel can't be hidden
31792     },
31793     
31794     show : function(){
31795         // center panel can't be hidden
31796     },
31797     
31798     getMinWidth: function(){
31799         return this.minWidth;
31800     },
31801     
31802     getMinHeight: function(){
31803         return this.minHeight;
31804     }
31805 });
31806
31807
31808 Roo.NorthLayoutRegion = function(mgr, config){
31809     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
31810     if(this.split){
31811         this.split.placement = Roo.SplitBar.TOP;
31812         this.split.orientation = Roo.SplitBar.VERTICAL;
31813         this.split.el.addClass("x-layout-split-v");
31814     }
31815     var size = config.initialSize || config.height;
31816     if(typeof size != "undefined"){
31817         this.el.setHeight(size);
31818     }
31819 };
31820 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
31821     orientation: Roo.SplitBar.VERTICAL,
31822     getBox : function(){
31823         if(this.collapsed){
31824             return this.collapsedEl.getBox();
31825         }
31826         var box = this.el.getBox();
31827         if(this.split){
31828             box.height += this.split.el.getHeight();
31829         }
31830         return box;
31831     },
31832     
31833     updateBox : function(box){
31834         if(this.split && !this.collapsed){
31835             box.height -= this.split.el.getHeight();
31836             this.split.el.setLeft(box.x);
31837             this.split.el.setTop(box.y+box.height);
31838             this.split.el.setWidth(box.width);
31839         }
31840         if(this.collapsed){
31841             this.updateBody(box.width, null);
31842         }
31843         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31844     }
31845 });
31846
31847 Roo.SouthLayoutRegion = function(mgr, config){
31848     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
31849     if(this.split){
31850         this.split.placement = Roo.SplitBar.BOTTOM;
31851         this.split.orientation = Roo.SplitBar.VERTICAL;
31852         this.split.el.addClass("x-layout-split-v");
31853     }
31854     var size = config.initialSize || config.height;
31855     if(typeof size != "undefined"){
31856         this.el.setHeight(size);
31857     }
31858 };
31859 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
31860     orientation: Roo.SplitBar.VERTICAL,
31861     getBox : function(){
31862         if(this.collapsed){
31863             return this.collapsedEl.getBox();
31864         }
31865         var box = this.el.getBox();
31866         if(this.split){
31867             var sh = this.split.el.getHeight();
31868             box.height += sh;
31869             box.y -= sh;
31870         }
31871         return box;
31872     },
31873     
31874     updateBox : function(box){
31875         if(this.split && !this.collapsed){
31876             var sh = this.split.el.getHeight();
31877             box.height -= sh;
31878             box.y += sh;
31879             this.split.el.setLeft(box.x);
31880             this.split.el.setTop(box.y-sh);
31881             this.split.el.setWidth(box.width);
31882         }
31883         if(this.collapsed){
31884             this.updateBody(box.width, null);
31885         }
31886         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31887     }
31888 });
31889
31890 Roo.EastLayoutRegion = function(mgr, config){
31891     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
31892     if(this.split){
31893         this.split.placement = Roo.SplitBar.RIGHT;
31894         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31895         this.split.el.addClass("x-layout-split-h");
31896     }
31897     var size = config.initialSize || config.width;
31898     if(typeof size != "undefined"){
31899         this.el.setWidth(size);
31900     }
31901 };
31902 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
31903     orientation: Roo.SplitBar.HORIZONTAL,
31904     getBox : function(){
31905         if(this.collapsed){
31906             return this.collapsedEl.getBox();
31907         }
31908         var box = this.el.getBox();
31909         if(this.split){
31910             var sw = this.split.el.getWidth();
31911             box.width += sw;
31912             box.x -= sw;
31913         }
31914         return box;
31915     },
31916
31917     updateBox : function(box){
31918         if(this.split && !this.collapsed){
31919             var sw = this.split.el.getWidth();
31920             box.width -= sw;
31921             this.split.el.setLeft(box.x);
31922             this.split.el.setTop(box.y);
31923             this.split.el.setHeight(box.height);
31924             box.x += sw;
31925         }
31926         if(this.collapsed){
31927             this.updateBody(null, box.height);
31928         }
31929         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31930     }
31931 });
31932
31933 Roo.WestLayoutRegion = function(mgr, config){
31934     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
31935     if(this.split){
31936         this.split.placement = Roo.SplitBar.LEFT;
31937         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31938         this.split.el.addClass("x-layout-split-h");
31939     }
31940     var size = config.initialSize || config.width;
31941     if(typeof size != "undefined"){
31942         this.el.setWidth(size);
31943     }
31944 };
31945 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
31946     orientation: Roo.SplitBar.HORIZONTAL,
31947     getBox : function(){
31948         if(this.collapsed){
31949             return this.collapsedEl.getBox();
31950         }
31951         var box = this.el.getBox();
31952         if(this.split){
31953             box.width += this.split.el.getWidth();
31954         }
31955         return box;
31956     },
31957     
31958     updateBox : function(box){
31959         if(this.split && !this.collapsed){
31960             var sw = this.split.el.getWidth();
31961             box.width -= sw;
31962             this.split.el.setLeft(box.x+box.width);
31963             this.split.el.setTop(box.y);
31964             this.split.el.setHeight(box.height);
31965         }
31966         if(this.collapsed){
31967             this.updateBody(null, box.height);
31968         }
31969         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31970     }
31971 });
31972 /*
31973  * Based on:
31974  * Ext JS Library 1.1.1
31975  * Copyright(c) 2006-2007, Ext JS, LLC.
31976  *
31977  * Originally Released Under LGPL - original licence link has changed is not relivant.
31978  *
31979  * Fork - LGPL
31980  * <script type="text/javascript">
31981  */
31982  
31983  
31984 /*
31985  * Private internal class for reading and applying state
31986  */
31987 Roo.LayoutStateManager = function(layout){
31988      // default empty state
31989      this.state = {
31990         north: {},
31991         south: {},
31992         east: {},
31993         west: {}       
31994     };
31995 };
31996
31997 Roo.LayoutStateManager.prototype = {
31998     init : function(layout, provider){
31999         this.provider = provider;
32000         var state = provider.get(layout.id+"-layout-state");
32001         if(state){
32002             var wasUpdating = layout.isUpdating();
32003             if(!wasUpdating){
32004                 layout.beginUpdate();
32005             }
32006             for(var key in state){
32007                 if(typeof state[key] != "function"){
32008                     var rstate = state[key];
32009                     var r = layout.getRegion(key);
32010                     if(r && rstate){
32011                         if(rstate.size){
32012                             r.resizeTo(rstate.size);
32013                         }
32014                         if(rstate.collapsed == true){
32015                             r.collapse(true);
32016                         }else{
32017                             r.expand(null, true);
32018                         }
32019                     }
32020                 }
32021             }
32022             if(!wasUpdating){
32023                 layout.endUpdate();
32024             }
32025             this.state = state; 
32026         }
32027         this.layout = layout;
32028         layout.on("regionresized", this.onRegionResized, this);
32029         layout.on("regioncollapsed", this.onRegionCollapsed, this);
32030         layout.on("regionexpanded", this.onRegionExpanded, this);
32031     },
32032     
32033     storeState : function(){
32034         this.provider.set(this.layout.id+"-layout-state", this.state);
32035     },
32036     
32037     onRegionResized : function(region, newSize){
32038         this.state[region.getPosition()].size = newSize;
32039         this.storeState();
32040     },
32041     
32042     onRegionCollapsed : function(region){
32043         this.state[region.getPosition()].collapsed = true;
32044         this.storeState();
32045     },
32046     
32047     onRegionExpanded : function(region){
32048         this.state[region.getPosition()].collapsed = false;
32049         this.storeState();
32050     }
32051 };/*
32052  * Based on:
32053  * Ext JS Library 1.1.1
32054  * Copyright(c) 2006-2007, Ext JS, LLC.
32055  *
32056  * Originally Released Under LGPL - original licence link has changed is not relivant.
32057  *
32058  * Fork - LGPL
32059  * <script type="text/javascript">
32060  */
32061 /**
32062  * @class Roo.ContentPanel
32063  * @extends Roo.util.Observable
32064  * A basic ContentPanel element.
32065  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
32066  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
32067  * @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
32068  * @cfg {Boolean}   closable      True if the panel can be closed/removed
32069  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
32070  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
32071  * @cfg {Toolbar}   toolbar       A toolbar for this panel
32072  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
32073  * @cfg {String} title          The title for this panel
32074  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
32075  * @cfg {String} url            Calls {@link #setUrl} with this value
32076  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
32077  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
32078  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
32079  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
32080
32081  * @constructor
32082  * Create a new ContentPanel.
32083  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
32084  * @param {String/Object} config A string to set only the title or a config object
32085  * @param {String} content (optional) Set the HTML content for this panel
32086  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
32087  */
32088 Roo.ContentPanel = function(el, config, content){
32089     
32090      
32091     /*
32092     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
32093         config = el;
32094         el = Roo.id();
32095     }
32096     if (config && config.parentLayout) { 
32097         el = config.parentLayout.el.createChild(); 
32098     }
32099     */
32100     if(el.autoCreate){ // xtype is available if this is called from factory
32101         config = el;
32102         el = Roo.id();
32103     }
32104     this.el = Roo.get(el);
32105     if(!this.el && config && config.autoCreate){
32106         if(typeof config.autoCreate == "object"){
32107             if(!config.autoCreate.id){
32108                 config.autoCreate.id = config.id||el;
32109             }
32110             this.el = Roo.DomHelper.append(document.body,
32111                         config.autoCreate, true);
32112         }else{
32113             this.el = Roo.DomHelper.append(document.body,
32114                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
32115         }
32116     }
32117     this.closable = false;
32118     this.loaded = false;
32119     this.active = false;
32120     if(typeof config == "string"){
32121         this.title = config;
32122     }else{
32123         Roo.apply(this, config);
32124     }
32125     
32126     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
32127         this.wrapEl = this.el.wrap();
32128         this.toolbar.container = this.el.insertSibling(false, 'before');
32129         this.toolbar = new Roo.Toolbar(this.toolbar);
32130     }
32131     
32132     
32133     
32134     if(this.resizeEl){
32135         this.resizeEl = Roo.get(this.resizeEl, true);
32136     }else{
32137         this.resizeEl = this.el;
32138     }
32139     this.addEvents({
32140         /**
32141          * @event activate
32142          * Fires when this panel is activated. 
32143          * @param {Roo.ContentPanel} this
32144          */
32145         "activate" : true,
32146         /**
32147          * @event deactivate
32148          * Fires when this panel is activated. 
32149          * @param {Roo.ContentPanel} this
32150          */
32151         "deactivate" : true,
32152
32153         /**
32154          * @event resize
32155          * Fires when this panel is resized if fitToFrame is true.
32156          * @param {Roo.ContentPanel} this
32157          * @param {Number} width The width after any component adjustments
32158          * @param {Number} height The height after any component adjustments
32159          */
32160         "resize" : true,
32161         
32162          /**
32163          * @event render
32164          * Fires when this tab is created
32165          * @param {Roo.ContentPanel} this
32166          */
32167         "render" : true
32168         
32169         
32170         
32171     });
32172     if(this.autoScroll){
32173         this.resizeEl.setStyle("overflow", "auto");
32174     } else {
32175         // fix randome scrolling
32176         this.el.on('scroll', function() {
32177             Roo.log('fix random scolling');
32178             this.scrollTo('top',0); 
32179         });
32180     }
32181     content = content || this.content;
32182     if(content){
32183         this.setContent(content);
32184     }
32185     if(config && config.url){
32186         this.setUrl(this.url, this.params, this.loadOnce);
32187     }
32188     
32189     
32190     
32191     Roo.ContentPanel.superclass.constructor.call(this);
32192     
32193     this.fireEvent('render', this);
32194 };
32195
32196 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
32197     tabTip:'',
32198     setRegion : function(region){
32199         this.region = region;
32200         if(region){
32201            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
32202         }else{
32203            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
32204         } 
32205     },
32206     
32207     /**
32208      * Returns the toolbar for this Panel if one was configured. 
32209      * @return {Roo.Toolbar} 
32210      */
32211     getToolbar : function(){
32212         return this.toolbar;
32213     },
32214     
32215     setActiveState : function(active){
32216         this.active = active;
32217         if(!active){
32218             this.fireEvent("deactivate", this);
32219         }else{
32220             this.fireEvent("activate", this);
32221         }
32222     },
32223     /**
32224      * Updates this panel's element
32225      * @param {String} content The new content
32226      * @param {Boolean} loadScripts (optional) true to look for and process scripts
32227     */
32228     setContent : function(content, loadScripts){
32229         this.el.update(content, loadScripts);
32230     },
32231
32232     ignoreResize : function(w, h){
32233         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
32234             return true;
32235         }else{
32236             this.lastSize = {width: w, height: h};
32237             return false;
32238         }
32239     },
32240     /**
32241      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
32242      * @return {Roo.UpdateManager} The UpdateManager
32243      */
32244     getUpdateManager : function(){
32245         return this.el.getUpdateManager();
32246     },
32247      /**
32248      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
32249      * @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:
32250 <pre><code>
32251 panel.load({
32252     url: "your-url.php",
32253     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
32254     callback: yourFunction,
32255     scope: yourObject, //(optional scope)
32256     discardUrl: false,
32257     nocache: false,
32258     text: "Loading...",
32259     timeout: 30,
32260     scripts: false
32261 });
32262 </code></pre>
32263      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
32264      * 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.
32265      * @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}
32266      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
32267      * @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.
32268      * @return {Roo.ContentPanel} this
32269      */
32270     load : function(){
32271         var um = this.el.getUpdateManager();
32272         um.update.apply(um, arguments);
32273         return this;
32274     },
32275
32276
32277     /**
32278      * 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.
32279      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
32280      * @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)
32281      * @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)
32282      * @return {Roo.UpdateManager} The UpdateManager
32283      */
32284     setUrl : function(url, params, loadOnce){
32285         if(this.refreshDelegate){
32286             this.removeListener("activate", this.refreshDelegate);
32287         }
32288         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
32289         this.on("activate", this.refreshDelegate);
32290         return this.el.getUpdateManager();
32291     },
32292     
32293     _handleRefresh : function(url, params, loadOnce){
32294         if(!loadOnce || !this.loaded){
32295             var updater = this.el.getUpdateManager();
32296             updater.update(url, params, this._setLoaded.createDelegate(this));
32297         }
32298     },
32299     
32300     _setLoaded : function(){
32301         this.loaded = true;
32302     }, 
32303     
32304     /**
32305      * Returns this panel's id
32306      * @return {String} 
32307      */
32308     getId : function(){
32309         return this.el.id;
32310     },
32311     
32312     /** 
32313      * Returns this panel's element - used by regiosn to add.
32314      * @return {Roo.Element} 
32315      */
32316     getEl : function(){
32317         return this.wrapEl || this.el;
32318     },
32319     
32320     adjustForComponents : function(width, height){
32321         if(this.resizeEl != this.el){
32322             width -= this.el.getFrameWidth('lr');
32323             height -= this.el.getFrameWidth('tb');
32324         }
32325         if(this.toolbar){
32326             var te = this.toolbar.getEl();
32327             height -= te.getHeight();
32328             te.setWidth(width);
32329         }
32330         if(this.adjustments){
32331             width += this.adjustments[0];
32332             height += this.adjustments[1];
32333         }
32334         return {"width": width, "height": height};
32335     },
32336     
32337     setSize : function(width, height){
32338         if(this.fitToFrame && !this.ignoreResize(width, height)){
32339             if(this.fitContainer && this.resizeEl != this.el){
32340                 this.el.setSize(width, height);
32341             }
32342             var size = this.adjustForComponents(width, height);
32343             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
32344             this.fireEvent('resize', this, size.width, size.height);
32345         }
32346     },
32347     
32348     /**
32349      * Returns this panel's title
32350      * @return {String} 
32351      */
32352     getTitle : function(){
32353         return this.title;
32354     },
32355     
32356     /**
32357      * Set this panel's title
32358      * @param {String} title
32359      */
32360     setTitle : function(title){
32361         this.title = title;
32362         if(this.region){
32363             this.region.updatePanelTitle(this, title);
32364         }
32365     },
32366     
32367     /**
32368      * Returns true is this panel was configured to be closable
32369      * @return {Boolean} 
32370      */
32371     isClosable : function(){
32372         return this.closable;
32373     },
32374     
32375     beforeSlide : function(){
32376         this.el.clip();
32377         this.resizeEl.clip();
32378     },
32379     
32380     afterSlide : function(){
32381         this.el.unclip();
32382         this.resizeEl.unclip();
32383     },
32384     
32385     /**
32386      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
32387      *   Will fail silently if the {@link #setUrl} method has not been called.
32388      *   This does not activate the panel, just updates its content.
32389      */
32390     refresh : function(){
32391         if(this.refreshDelegate){
32392            this.loaded = false;
32393            this.refreshDelegate();
32394         }
32395     },
32396     
32397     /**
32398      * Destroys this panel
32399      */
32400     destroy : function(){
32401         this.el.removeAllListeners();
32402         var tempEl = document.createElement("span");
32403         tempEl.appendChild(this.el.dom);
32404         tempEl.innerHTML = "";
32405         this.el.remove();
32406         this.el = null;
32407     },
32408     
32409     /**
32410      * form - if the content panel contains a form - this is a reference to it.
32411      * @type {Roo.form.Form}
32412      */
32413     form : false,
32414     /**
32415      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
32416      *    This contains a reference to it.
32417      * @type {Roo.View}
32418      */
32419     view : false,
32420     
32421       /**
32422      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
32423      * <pre><code>
32424
32425 layout.addxtype({
32426        xtype : 'Form',
32427        items: [ .... ]
32428    }
32429 );
32430
32431 </code></pre>
32432      * @param {Object} cfg Xtype definition of item to add.
32433      */
32434     
32435     addxtype : function(cfg) {
32436         // add form..
32437         if (cfg.xtype.match(/^Form$/)) {
32438             var el = this.el.createChild();
32439
32440             this.form = new  Roo.form.Form(cfg);
32441             
32442             
32443             if ( this.form.allItems.length) this.form.render(el.dom);
32444             return this.form;
32445         }
32446         // should only have one of theses..
32447         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
32448             // views..
32449             cfg.el = this.el.appendChild(document.createElement("div"));
32450             // factory?
32451             
32452             var ret = new Roo.factory(cfg);
32453             ret.render && ret.render(false, ''); // render blank..
32454             this.view = ret;
32455             return ret;
32456         }
32457         return false;
32458     }
32459 });
32460
32461 /**
32462  * @class Roo.GridPanel
32463  * @extends Roo.ContentPanel
32464  * @constructor
32465  * Create a new GridPanel.
32466  * @param {Roo.grid.Grid} grid The grid for this panel
32467  * @param {String/Object} config A string to set only the panel's title, or a config object
32468  */
32469 Roo.GridPanel = function(grid, config){
32470     
32471   
32472     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
32473         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
32474         
32475     this.wrapper.dom.appendChild(grid.getGridEl().dom);
32476     
32477     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
32478     
32479     if(this.toolbar){
32480         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
32481     }
32482     // xtype created footer. - not sure if will work as we normally have to render first..
32483     if (this.footer && !this.footer.el && this.footer.xtype) {
32484         
32485         this.footer.container = this.grid.getView().getFooterPanel(true);
32486         this.footer.dataSource = this.grid.dataSource;
32487         this.footer = Roo.factory(this.footer, Roo);
32488         
32489     }
32490     
32491     grid.monitorWindowResize = false; // turn off autosizing
32492     grid.autoHeight = false;
32493     grid.autoWidth = false;
32494     this.grid = grid;
32495     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
32496 };
32497
32498 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
32499     getId : function(){
32500         return this.grid.id;
32501     },
32502     
32503     /**
32504      * Returns the grid for this panel
32505      * @return {Roo.grid.Grid} 
32506      */
32507     getGrid : function(){
32508         return this.grid;    
32509     },
32510     
32511     setSize : function(width, height){
32512         if(!this.ignoreResize(width, height)){
32513             var grid = this.grid;
32514             var size = this.adjustForComponents(width, height);
32515             grid.getGridEl().setSize(size.width, size.height);
32516             grid.autoSize();
32517         }
32518     },
32519     
32520     beforeSlide : function(){
32521         this.grid.getView().scroller.clip();
32522     },
32523     
32524     afterSlide : function(){
32525         this.grid.getView().scroller.unclip();
32526     },
32527     
32528     destroy : function(){
32529         this.grid.destroy();
32530         delete this.grid;
32531         Roo.GridPanel.superclass.destroy.call(this); 
32532     }
32533 });
32534
32535
32536 /**
32537  * @class Roo.NestedLayoutPanel
32538  * @extends Roo.ContentPanel
32539  * @constructor
32540  * Create a new NestedLayoutPanel.
32541  * 
32542  * 
32543  * @param {Roo.BorderLayout} layout The layout for this panel
32544  * @param {String/Object} config A string to set only the title or a config object
32545  */
32546 Roo.NestedLayoutPanel = function(layout, config)
32547 {
32548     // construct with only one argument..
32549     /* FIXME - implement nicer consturctors
32550     if (layout.layout) {
32551         config = layout;
32552         layout = config.layout;
32553         delete config.layout;
32554     }
32555     if (layout.xtype && !layout.getEl) {
32556         // then layout needs constructing..
32557         layout = Roo.factory(layout, Roo);
32558     }
32559     */
32560     
32561     
32562     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
32563     
32564     layout.monitorWindowResize = false; // turn off autosizing
32565     this.layout = layout;
32566     this.layout.getEl().addClass("x-layout-nested-layout");
32567     
32568     
32569     
32570     
32571 };
32572
32573 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
32574
32575     setSize : function(width, height){
32576         if(!this.ignoreResize(width, height)){
32577             var size = this.adjustForComponents(width, height);
32578             var el = this.layout.getEl();
32579             el.setSize(size.width, size.height);
32580             var touch = el.dom.offsetWidth;
32581             this.layout.layout();
32582             // ie requires a double layout on the first pass
32583             if(Roo.isIE && !this.initialized){
32584                 this.initialized = true;
32585                 this.layout.layout();
32586             }
32587         }
32588     },
32589     
32590     // activate all subpanels if not currently active..
32591     
32592     setActiveState : function(active){
32593         this.active = active;
32594         if(!active){
32595             this.fireEvent("deactivate", this);
32596             return;
32597         }
32598         
32599         this.fireEvent("activate", this);
32600         // not sure if this should happen before or after..
32601         if (!this.layout) {
32602             return; // should not happen..
32603         }
32604         var reg = false;
32605         for (var r in this.layout.regions) {
32606             reg = this.layout.getRegion(r);
32607             if (reg.getActivePanel()) {
32608                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
32609                 reg.setActivePanel(reg.getActivePanel());
32610                 continue;
32611             }
32612             if (!reg.panels.length) {
32613                 continue;
32614             }
32615             reg.showPanel(reg.getPanel(0));
32616         }
32617         
32618         
32619         
32620         
32621     },
32622     
32623     /**
32624      * Returns the nested BorderLayout for this panel
32625      * @return {Roo.BorderLayout} 
32626      */
32627     getLayout : function(){
32628         return this.layout;
32629     },
32630     
32631      /**
32632      * Adds a xtype elements to the layout of the nested panel
32633      * <pre><code>
32634
32635 panel.addxtype({
32636        xtype : 'ContentPanel',
32637        region: 'west',
32638        items: [ .... ]
32639    }
32640 );
32641
32642 panel.addxtype({
32643         xtype : 'NestedLayoutPanel',
32644         region: 'west',
32645         layout: {
32646            center: { },
32647            west: { }   
32648         },
32649         items : [ ... list of content panels or nested layout panels.. ]
32650    }
32651 );
32652 </code></pre>
32653      * @param {Object} cfg Xtype definition of item to add.
32654      */
32655     addxtype : function(cfg) {
32656         return this.layout.addxtype(cfg);
32657     
32658     }
32659 });
32660
32661 Roo.ScrollPanel = function(el, config, content){
32662     config = config || {};
32663     config.fitToFrame = true;
32664     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
32665     
32666     this.el.dom.style.overflow = "hidden";
32667     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
32668     this.el.removeClass("x-layout-inactive-content");
32669     this.el.on("mousewheel", this.onWheel, this);
32670
32671     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
32672     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
32673     up.unselectable(); down.unselectable();
32674     up.on("click", this.scrollUp, this);
32675     down.on("click", this.scrollDown, this);
32676     up.addClassOnOver("x-scroller-btn-over");
32677     down.addClassOnOver("x-scroller-btn-over");
32678     up.addClassOnClick("x-scroller-btn-click");
32679     down.addClassOnClick("x-scroller-btn-click");
32680     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
32681
32682     this.resizeEl = this.el;
32683     this.el = wrap; this.up = up; this.down = down;
32684 };
32685
32686 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
32687     increment : 100,
32688     wheelIncrement : 5,
32689     scrollUp : function(){
32690         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
32691     },
32692
32693     scrollDown : function(){
32694         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
32695     },
32696
32697     afterScroll : function(){
32698         var el = this.resizeEl;
32699         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
32700         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
32701         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
32702     },
32703
32704     setSize : function(){
32705         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
32706         this.afterScroll();
32707     },
32708
32709     onWheel : function(e){
32710         var d = e.getWheelDelta();
32711         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
32712         this.afterScroll();
32713         e.stopEvent();
32714     },
32715
32716     setContent : function(content, loadScripts){
32717         this.resizeEl.update(content, loadScripts);
32718     }
32719
32720 });
32721
32722
32723
32724
32725
32726
32727
32728
32729
32730 /**
32731  * @class Roo.TreePanel
32732  * @extends Roo.ContentPanel
32733  * @constructor
32734  * Create a new TreePanel. - defaults to fit/scoll contents.
32735  * @param {String/Object} config A string to set only the panel's title, or a config object
32736  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
32737  */
32738 Roo.TreePanel = function(config){
32739     var el = config.el;
32740     var tree = config.tree;
32741     delete config.tree; 
32742     delete config.el; // hopefull!
32743     
32744     // wrapper for IE7 strict & safari scroll issue
32745     
32746     var treeEl = el.createChild();
32747     config.resizeEl = treeEl;
32748     
32749     
32750     
32751     Roo.TreePanel.superclass.constructor.call(this, el, config);
32752  
32753  
32754     this.tree = new Roo.tree.TreePanel(treeEl , tree);
32755     //console.log(tree);
32756     this.on('activate', function()
32757     {
32758         if (this.tree.rendered) {
32759             return;
32760         }
32761         //console.log('render tree');
32762         this.tree.render();
32763     });
32764     
32765     this.on('resize',  function (cp, w, h) {
32766             this.tree.innerCt.setWidth(w);
32767             this.tree.innerCt.setHeight(h);
32768             this.tree.innerCt.setStyle('overflow-y', 'auto');
32769     });
32770
32771         
32772     
32773 };
32774
32775 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
32776     fitToFrame : true,
32777     autoScroll : true
32778 });
32779
32780
32781
32782
32783
32784
32785
32786
32787
32788
32789
32790 /*
32791  * Based on:
32792  * Ext JS Library 1.1.1
32793  * Copyright(c) 2006-2007, Ext JS, LLC.
32794  *
32795  * Originally Released Under LGPL - original licence link has changed is not relivant.
32796  *
32797  * Fork - LGPL
32798  * <script type="text/javascript">
32799  */
32800  
32801
32802 /**
32803  * @class Roo.ReaderLayout
32804  * @extends Roo.BorderLayout
32805  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
32806  * center region containing two nested regions (a top one for a list view and one for item preview below),
32807  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
32808  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
32809  * expedites the setup of the overall layout and regions for this common application style.
32810  * Example:
32811  <pre><code>
32812 var reader = new Roo.ReaderLayout();
32813 var CP = Roo.ContentPanel;  // shortcut for adding
32814
32815 reader.beginUpdate();
32816 reader.add("north", new CP("north", "North"));
32817 reader.add("west", new CP("west", {title: "West"}));
32818 reader.add("east", new CP("east", {title: "East"}));
32819
32820 reader.regions.listView.add(new CP("listView", "List"));
32821 reader.regions.preview.add(new CP("preview", "Preview"));
32822 reader.endUpdate();
32823 </code></pre>
32824 * @constructor
32825 * Create a new ReaderLayout
32826 * @param {Object} config Configuration options
32827 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
32828 * document.body if omitted)
32829 */
32830 Roo.ReaderLayout = function(config, renderTo){
32831     var c = config || {size:{}};
32832     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
32833         north: c.north !== false ? Roo.apply({
32834             split:false,
32835             initialSize: 32,
32836             titlebar: false
32837         }, c.north) : false,
32838         west: c.west !== false ? Roo.apply({
32839             split:true,
32840             initialSize: 200,
32841             minSize: 175,
32842             maxSize: 400,
32843             titlebar: true,
32844             collapsible: true,
32845             animate: true,
32846             margins:{left:5,right:0,bottom:5,top:5},
32847             cmargins:{left:5,right:5,bottom:5,top:5}
32848         }, c.west) : false,
32849         east: c.east !== false ? Roo.apply({
32850             split:true,
32851             initialSize: 200,
32852             minSize: 175,
32853             maxSize: 400,
32854             titlebar: true,
32855             collapsible: true,
32856             animate: true,
32857             margins:{left:0,right:5,bottom:5,top:5},
32858             cmargins:{left:5,right:5,bottom:5,top:5}
32859         }, c.east) : false,
32860         center: Roo.apply({
32861             tabPosition: 'top',
32862             autoScroll:false,
32863             closeOnTab: true,
32864             titlebar:false,
32865             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
32866         }, c.center)
32867     });
32868
32869     this.el.addClass('x-reader');
32870
32871     this.beginUpdate();
32872
32873     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
32874         south: c.preview !== false ? Roo.apply({
32875             split:true,
32876             initialSize: 200,
32877             minSize: 100,
32878             autoScroll:true,
32879             collapsible:true,
32880             titlebar: true,
32881             cmargins:{top:5,left:0, right:0, bottom:0}
32882         }, c.preview) : false,
32883         center: Roo.apply({
32884             autoScroll:false,
32885             titlebar:false,
32886             minHeight:200
32887         }, c.listView)
32888     });
32889     this.add('center', new Roo.NestedLayoutPanel(inner,
32890             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
32891
32892     this.endUpdate();
32893
32894     this.regions.preview = inner.getRegion('south');
32895     this.regions.listView = inner.getRegion('center');
32896 };
32897
32898 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
32899  * Based on:
32900  * Ext JS Library 1.1.1
32901  * Copyright(c) 2006-2007, Ext JS, LLC.
32902  *
32903  * Originally Released Under LGPL - original licence link has changed is not relivant.
32904  *
32905  * Fork - LGPL
32906  * <script type="text/javascript">
32907  */
32908  
32909 /**
32910  * @class Roo.grid.Grid
32911  * @extends Roo.util.Observable
32912  * This class represents the primary interface of a component based grid control.
32913  * <br><br>Usage:<pre><code>
32914  var grid = new Roo.grid.Grid("my-container-id", {
32915      ds: myDataStore,
32916      cm: myColModel,
32917      selModel: mySelectionModel,
32918      autoSizeColumns: true,
32919      monitorWindowResize: false,
32920      trackMouseOver: true
32921  });
32922  // set any options
32923  grid.render();
32924  * </code></pre>
32925  * <b>Common Problems:</b><br/>
32926  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
32927  * element will correct this<br/>
32928  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
32929  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
32930  * are unpredictable.<br/>
32931  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
32932  * grid to calculate dimensions/offsets.<br/>
32933   * @constructor
32934  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
32935  * The container MUST have some type of size defined for the grid to fill. The container will be
32936  * automatically set to position relative if it isn't already.
32937  * @param {Object} config A config object that sets properties on this grid.
32938  */
32939 Roo.grid.Grid = function(container, config){
32940         // initialize the container
32941         this.container = Roo.get(container);
32942         this.container.update("");
32943         this.container.setStyle("overflow", "hidden");
32944     this.container.addClass('x-grid-container');
32945
32946     this.id = this.container.id;
32947
32948     Roo.apply(this, config);
32949     // check and correct shorthanded configs
32950     if(this.ds){
32951         this.dataSource = this.ds;
32952         delete this.ds;
32953     }
32954     if(this.cm){
32955         this.colModel = this.cm;
32956         delete this.cm;
32957     }
32958     if(this.sm){
32959         this.selModel = this.sm;
32960         delete this.sm;
32961     }
32962
32963     if (this.selModel) {
32964         this.selModel = Roo.factory(this.selModel, Roo.grid);
32965         this.sm = this.selModel;
32966         this.sm.xmodule = this.xmodule || false;
32967     }
32968     if (typeof(this.colModel.config) == 'undefined') {
32969         this.colModel = new Roo.grid.ColumnModel(this.colModel);
32970         this.cm = this.colModel;
32971         this.cm.xmodule = this.xmodule || false;
32972     }
32973     if (this.dataSource) {
32974         this.dataSource= Roo.factory(this.dataSource, Roo.data);
32975         this.ds = this.dataSource;
32976         this.ds.xmodule = this.xmodule || false;
32977          
32978     }
32979     
32980     
32981     
32982     if(this.width){
32983         this.container.setWidth(this.width);
32984     }
32985
32986     if(this.height){
32987         this.container.setHeight(this.height);
32988     }
32989     /** @private */
32990         this.addEvents({
32991         // raw events
32992         /**
32993          * @event click
32994          * The raw click event for the entire grid.
32995          * @param {Roo.EventObject} e
32996          */
32997         "click" : true,
32998         /**
32999          * @event dblclick
33000          * The raw dblclick event for the entire grid.
33001          * @param {Roo.EventObject} e
33002          */
33003         "dblclick" : true,
33004         /**
33005          * @event contextmenu
33006          * The raw contextmenu event for the entire grid.
33007          * @param {Roo.EventObject} e
33008          */
33009         "contextmenu" : true,
33010         /**
33011          * @event mousedown
33012          * The raw mousedown event for the entire grid.
33013          * @param {Roo.EventObject} e
33014          */
33015         "mousedown" : true,
33016         /**
33017          * @event mouseup
33018          * The raw mouseup event for the entire grid.
33019          * @param {Roo.EventObject} e
33020          */
33021         "mouseup" : true,
33022         /**
33023          * @event mouseover
33024          * The raw mouseover event for the entire grid.
33025          * @param {Roo.EventObject} e
33026          */
33027         "mouseover" : true,
33028         /**
33029          * @event mouseout
33030          * The raw mouseout event for the entire grid.
33031          * @param {Roo.EventObject} e
33032          */
33033         "mouseout" : true,
33034         /**
33035          * @event keypress
33036          * The raw keypress event for the entire grid.
33037          * @param {Roo.EventObject} e
33038          */
33039         "keypress" : true,
33040         /**
33041          * @event keydown
33042          * The raw keydown event for the entire grid.
33043          * @param {Roo.EventObject} e
33044          */
33045         "keydown" : true,
33046
33047         // custom events
33048
33049         /**
33050          * @event cellclick
33051          * Fires when a cell is clicked
33052          * @param {Grid} this
33053          * @param {Number} rowIndex
33054          * @param {Number} columnIndex
33055          * @param {Roo.EventObject} e
33056          */
33057         "cellclick" : true,
33058         /**
33059          * @event celldblclick
33060          * Fires when a cell is double clicked
33061          * @param {Grid} this
33062          * @param {Number} rowIndex
33063          * @param {Number} columnIndex
33064          * @param {Roo.EventObject} e
33065          */
33066         "celldblclick" : true,
33067         /**
33068          * @event rowclick
33069          * Fires when a row is clicked
33070          * @param {Grid} this
33071          * @param {Number} rowIndex
33072          * @param {Roo.EventObject} e
33073          */
33074         "rowclick" : true,
33075         /**
33076          * @event rowdblclick
33077          * Fires when a row is double clicked
33078          * @param {Grid} this
33079          * @param {Number} rowIndex
33080          * @param {Roo.EventObject} e
33081          */
33082         "rowdblclick" : true,
33083         /**
33084          * @event headerclick
33085          * Fires when a header is clicked
33086          * @param {Grid} this
33087          * @param {Number} columnIndex
33088          * @param {Roo.EventObject} e
33089          */
33090         "headerclick" : true,
33091         /**
33092          * @event headerdblclick
33093          * Fires when a header cell is double clicked
33094          * @param {Grid} this
33095          * @param {Number} columnIndex
33096          * @param {Roo.EventObject} e
33097          */
33098         "headerdblclick" : true,
33099         /**
33100          * @event rowcontextmenu
33101          * Fires when a row is right clicked
33102          * @param {Grid} this
33103          * @param {Number} rowIndex
33104          * @param {Roo.EventObject} e
33105          */
33106         "rowcontextmenu" : true,
33107         /**
33108          * @event cellcontextmenu
33109          * Fires when a cell is right clicked
33110          * @param {Grid} this
33111          * @param {Number} rowIndex
33112          * @param {Number} cellIndex
33113          * @param {Roo.EventObject} e
33114          */
33115          "cellcontextmenu" : true,
33116         /**
33117          * @event headercontextmenu
33118          * Fires when a header is right clicked
33119          * @param {Grid} this
33120          * @param {Number} columnIndex
33121          * @param {Roo.EventObject} e
33122          */
33123         "headercontextmenu" : true,
33124         /**
33125          * @event bodyscroll
33126          * Fires when the body element is scrolled
33127          * @param {Number} scrollLeft
33128          * @param {Number} scrollTop
33129          */
33130         "bodyscroll" : true,
33131         /**
33132          * @event columnresize
33133          * Fires when the user resizes a column
33134          * @param {Number} columnIndex
33135          * @param {Number} newSize
33136          */
33137         "columnresize" : true,
33138         /**
33139          * @event columnmove
33140          * Fires when the user moves a column
33141          * @param {Number} oldIndex
33142          * @param {Number} newIndex
33143          */
33144         "columnmove" : true,
33145         /**
33146          * @event startdrag
33147          * Fires when row(s) start being dragged
33148          * @param {Grid} this
33149          * @param {Roo.GridDD} dd The drag drop object
33150          * @param {event} e The raw browser event
33151          */
33152         "startdrag" : true,
33153         /**
33154          * @event enddrag
33155          * Fires when a drag operation is complete
33156          * @param {Grid} this
33157          * @param {Roo.GridDD} dd The drag drop object
33158          * @param {event} e The raw browser event
33159          */
33160         "enddrag" : true,
33161         /**
33162          * @event dragdrop
33163          * Fires when dragged row(s) are dropped on a valid DD target
33164          * @param {Grid} this
33165          * @param {Roo.GridDD} dd The drag drop object
33166          * @param {String} targetId The target drag drop object
33167          * @param {event} e The raw browser event
33168          */
33169         "dragdrop" : true,
33170         /**
33171          * @event dragover
33172          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
33173          * @param {Grid} this
33174          * @param {Roo.GridDD} dd The drag drop object
33175          * @param {String} targetId The target drag drop object
33176          * @param {event} e The raw browser event
33177          */
33178         "dragover" : true,
33179         /**
33180          * @event dragenter
33181          *  Fires when the dragged row(s) first cross another DD target while being dragged
33182          * @param {Grid} this
33183          * @param {Roo.GridDD} dd The drag drop object
33184          * @param {String} targetId The target drag drop object
33185          * @param {event} e The raw browser event
33186          */
33187         "dragenter" : true,
33188         /**
33189          * @event dragout
33190          * Fires when the dragged row(s) leave another DD target while being dragged
33191          * @param {Grid} this
33192          * @param {Roo.GridDD} dd The drag drop object
33193          * @param {String} targetId The target drag drop object
33194          * @param {event} e The raw browser event
33195          */
33196         "dragout" : true,
33197         /**
33198          * @event rowclass
33199          * Fires when a row is rendered, so you can change add a style to it.
33200          * @param {GridView} gridview   The grid view
33201          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
33202          */
33203         'rowclass' : true,
33204
33205         /**
33206          * @event render
33207          * Fires when the grid is rendered
33208          * @param {Grid} grid
33209          */
33210         'render' : true
33211     });
33212
33213     Roo.grid.Grid.superclass.constructor.call(this);
33214 };
33215 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
33216     
33217     /**
33218      * @cfg {String} ddGroup - drag drop group.
33219      */
33220
33221     /**
33222      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
33223      */
33224     minColumnWidth : 25,
33225
33226     /**
33227      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
33228      * <b>on initial render.</b> It is more efficient to explicitly size the columns
33229      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
33230      */
33231     autoSizeColumns : false,
33232
33233     /**
33234      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
33235      */
33236     autoSizeHeaders : true,
33237
33238     /**
33239      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
33240      */
33241     monitorWindowResize : true,
33242
33243     /**
33244      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
33245      * rows measured to get a columns size. Default is 0 (all rows).
33246      */
33247     maxRowsToMeasure : 0,
33248
33249     /**
33250      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
33251      */
33252     trackMouseOver : true,
33253
33254     /**
33255     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
33256     */
33257     
33258     /**
33259     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
33260     */
33261     enableDragDrop : false,
33262     
33263     /**
33264     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
33265     */
33266     enableColumnMove : true,
33267     
33268     /**
33269     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
33270     */
33271     enableColumnHide : true,
33272     
33273     /**
33274     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
33275     */
33276     enableRowHeightSync : false,
33277     
33278     /**
33279     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
33280     */
33281     stripeRows : true,
33282     
33283     /**
33284     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
33285     */
33286     autoHeight : false,
33287
33288     /**
33289      * @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.
33290      */
33291     autoExpandColumn : false,
33292
33293     /**
33294     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
33295     * Default is 50.
33296     */
33297     autoExpandMin : 50,
33298
33299     /**
33300     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
33301     */
33302     autoExpandMax : 1000,
33303
33304     /**
33305     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
33306     */
33307     view : null,
33308
33309     /**
33310     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
33311     */
33312     loadMask : false,
33313     /**
33314     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
33315     */
33316     dropTarget: false,
33317     
33318    
33319     
33320     // private
33321     rendered : false,
33322
33323     /**
33324     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
33325     * of a fixed width. Default is false.
33326     */
33327     /**
33328     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
33329     */
33330     /**
33331      * Called once after all setup has been completed and the grid is ready to be rendered.
33332      * @return {Roo.grid.Grid} this
33333      */
33334     render : function()
33335     {
33336         var c = this.container;
33337         // try to detect autoHeight/width mode
33338         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
33339             this.autoHeight = true;
33340         }
33341         var view = this.getView();
33342         view.init(this);
33343
33344         c.on("click", this.onClick, this);
33345         c.on("dblclick", this.onDblClick, this);
33346         c.on("contextmenu", this.onContextMenu, this);
33347         c.on("keydown", this.onKeyDown, this);
33348
33349         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
33350
33351         this.getSelectionModel().init(this);
33352
33353         view.render();
33354
33355         if(this.loadMask){
33356             this.loadMask = new Roo.LoadMask(this.container,
33357                     Roo.apply({store:this.dataSource}, this.loadMask));
33358         }
33359         
33360         
33361         if (this.toolbar && this.toolbar.xtype) {
33362             this.toolbar.container = this.getView().getHeaderPanel(true);
33363             this.toolbar = new Roo.Toolbar(this.toolbar);
33364         }
33365         if (this.footer && this.footer.xtype) {
33366             this.footer.dataSource = this.getDataSource();
33367             this.footer.container = this.getView().getFooterPanel(true);
33368             this.footer = Roo.factory(this.footer, Roo);
33369         }
33370         if (this.dropTarget && this.dropTarget.xtype) {
33371             delete this.dropTarget.xtype;
33372             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
33373         }
33374         
33375         
33376         this.rendered = true;
33377         this.fireEvent('render', this);
33378         return this;
33379     },
33380
33381         /**
33382          * Reconfigures the grid to use a different Store and Column Model.
33383          * The View will be bound to the new objects and refreshed.
33384          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
33385          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
33386          */
33387     reconfigure : function(dataSource, colModel){
33388         if(this.loadMask){
33389             this.loadMask.destroy();
33390             this.loadMask = new Roo.LoadMask(this.container,
33391                     Roo.apply({store:dataSource}, this.loadMask));
33392         }
33393         this.view.bind(dataSource, colModel);
33394         this.dataSource = dataSource;
33395         this.colModel = colModel;
33396         this.view.refresh(true);
33397     },
33398
33399     // private
33400     onKeyDown : function(e){
33401         this.fireEvent("keydown", e);
33402     },
33403
33404     /**
33405      * Destroy this grid.
33406      * @param {Boolean} removeEl True to remove the element
33407      */
33408     destroy : function(removeEl, keepListeners){
33409         if(this.loadMask){
33410             this.loadMask.destroy();
33411         }
33412         var c = this.container;
33413         c.removeAllListeners();
33414         this.view.destroy();
33415         this.colModel.purgeListeners();
33416         if(!keepListeners){
33417             this.purgeListeners();
33418         }
33419         c.update("");
33420         if(removeEl === true){
33421             c.remove();
33422         }
33423     },
33424
33425     // private
33426     processEvent : function(name, e){
33427         this.fireEvent(name, e);
33428         var t = e.getTarget();
33429         var v = this.view;
33430         var header = v.findHeaderIndex(t);
33431         if(header !== false){
33432             this.fireEvent("header" + name, this, header, e);
33433         }else{
33434             var row = v.findRowIndex(t);
33435             var cell = v.findCellIndex(t);
33436             if(row !== false){
33437                 this.fireEvent("row" + name, this, row, e);
33438                 if(cell !== false){
33439                     this.fireEvent("cell" + name, this, row, cell, e);
33440                 }
33441             }
33442         }
33443     },
33444
33445     // private
33446     onClick : function(e){
33447         this.processEvent("click", e);
33448     },
33449
33450     // private
33451     onContextMenu : function(e, t){
33452         this.processEvent("contextmenu", e);
33453     },
33454
33455     // private
33456     onDblClick : function(e){
33457         this.processEvent("dblclick", e);
33458     },
33459
33460     // private
33461     walkCells : function(row, col, step, fn, scope){
33462         var cm = this.colModel, clen = cm.getColumnCount();
33463         var ds = this.dataSource, rlen = ds.getCount(), first = true;
33464         if(step < 0){
33465             if(col < 0){
33466                 row--;
33467                 first = false;
33468             }
33469             while(row >= 0){
33470                 if(!first){
33471                     col = clen-1;
33472                 }
33473                 first = false;
33474                 while(col >= 0){
33475                     if(fn.call(scope || this, row, col, cm) === true){
33476                         return [row, col];
33477                     }
33478                     col--;
33479                 }
33480                 row--;
33481             }
33482         } else {
33483             if(col >= clen){
33484                 row++;
33485                 first = false;
33486             }
33487             while(row < rlen){
33488                 if(!first){
33489                     col = 0;
33490                 }
33491                 first = false;
33492                 while(col < clen){
33493                     if(fn.call(scope || this, row, col, cm) === true){
33494                         return [row, col];
33495                     }
33496                     col++;
33497                 }
33498                 row++;
33499             }
33500         }
33501         return null;
33502     },
33503
33504     // private
33505     getSelections : function(){
33506         return this.selModel.getSelections();
33507     },
33508
33509     /**
33510      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
33511      * but if manual update is required this method will initiate it.
33512      */
33513     autoSize : function(){
33514         if(this.rendered){
33515             this.view.layout();
33516             if(this.view.adjustForScroll){
33517                 this.view.adjustForScroll();
33518             }
33519         }
33520     },
33521
33522     /**
33523      * Returns the grid's underlying element.
33524      * @return {Element} The element
33525      */
33526     getGridEl : function(){
33527         return this.container;
33528     },
33529
33530     // private for compatibility, overridden by editor grid
33531     stopEditing : function(){},
33532
33533     /**
33534      * Returns the grid's SelectionModel.
33535      * @return {SelectionModel}
33536      */
33537     getSelectionModel : function(){
33538         if(!this.selModel){
33539             this.selModel = new Roo.grid.RowSelectionModel();
33540         }
33541         return this.selModel;
33542     },
33543
33544     /**
33545      * Returns the grid's DataSource.
33546      * @return {DataSource}
33547      */
33548     getDataSource : function(){
33549         return this.dataSource;
33550     },
33551
33552     /**
33553      * Returns the grid's ColumnModel.
33554      * @return {ColumnModel}
33555      */
33556     getColumnModel : function(){
33557         return this.colModel;
33558     },
33559
33560     /**
33561      * Returns the grid's GridView object.
33562      * @return {GridView}
33563      */
33564     getView : function(){
33565         if(!this.view){
33566             this.view = new Roo.grid.GridView(this.viewConfig);
33567         }
33568         return this.view;
33569     },
33570     /**
33571      * Called to get grid's drag proxy text, by default returns this.ddText.
33572      * @return {String}
33573      */
33574     getDragDropText : function(){
33575         var count = this.selModel.getCount();
33576         return String.format(this.ddText, count, count == 1 ? '' : 's');
33577     }
33578 });
33579 /**
33580  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
33581  * %0 is replaced with the number of selected rows.
33582  * @type String
33583  */
33584 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
33585  * Based on:
33586  * Ext JS Library 1.1.1
33587  * Copyright(c) 2006-2007, Ext JS, LLC.
33588  *
33589  * Originally Released Under LGPL - original licence link has changed is not relivant.
33590  *
33591  * Fork - LGPL
33592  * <script type="text/javascript">
33593  */
33594  
33595 Roo.grid.AbstractGridView = function(){
33596         this.grid = null;
33597         
33598         this.events = {
33599             "beforerowremoved" : true,
33600             "beforerowsinserted" : true,
33601             "beforerefresh" : true,
33602             "rowremoved" : true,
33603             "rowsinserted" : true,
33604             "rowupdated" : true,
33605             "refresh" : true
33606         };
33607     Roo.grid.AbstractGridView.superclass.constructor.call(this);
33608 };
33609
33610 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
33611     rowClass : "x-grid-row",
33612     cellClass : "x-grid-cell",
33613     tdClass : "x-grid-td",
33614     hdClass : "x-grid-hd",
33615     splitClass : "x-grid-hd-split",
33616     
33617         init: function(grid){
33618         this.grid = grid;
33619                 var cid = this.grid.getGridEl().id;
33620         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
33621         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
33622         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
33623         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
33624         },
33625         
33626         getColumnRenderers : function(){
33627         var renderers = [];
33628         var cm = this.grid.colModel;
33629         var colCount = cm.getColumnCount();
33630         for(var i = 0; i < colCount; i++){
33631             renderers[i] = cm.getRenderer(i);
33632         }
33633         return renderers;
33634     },
33635     
33636     getColumnIds : function(){
33637         var ids = [];
33638         var cm = this.grid.colModel;
33639         var colCount = cm.getColumnCount();
33640         for(var i = 0; i < colCount; i++){
33641             ids[i] = cm.getColumnId(i);
33642         }
33643         return ids;
33644     },
33645     
33646     getDataIndexes : function(){
33647         if(!this.indexMap){
33648             this.indexMap = this.buildIndexMap();
33649         }
33650         return this.indexMap.colToData;
33651     },
33652     
33653     getColumnIndexByDataIndex : function(dataIndex){
33654         if(!this.indexMap){
33655             this.indexMap = this.buildIndexMap();
33656         }
33657         return this.indexMap.dataToCol[dataIndex];
33658     },
33659     
33660     /**
33661      * Set a css style for a column dynamically. 
33662      * @param {Number} colIndex The index of the column
33663      * @param {String} name The css property name
33664      * @param {String} value The css value
33665      */
33666     setCSSStyle : function(colIndex, name, value){
33667         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
33668         Roo.util.CSS.updateRule(selector, name, value);
33669     },
33670     
33671     generateRules : function(cm){
33672         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
33673         Roo.util.CSS.removeStyleSheet(rulesId);
33674         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33675             var cid = cm.getColumnId(i);
33676             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
33677                          this.tdSelector, cid, " {\n}\n",
33678                          this.hdSelector, cid, " {\n}\n",
33679                          this.splitSelector, cid, " {\n}\n");
33680         }
33681         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33682     }
33683 });/*
33684  * Based on:
33685  * Ext JS Library 1.1.1
33686  * Copyright(c) 2006-2007, Ext JS, LLC.
33687  *
33688  * Originally Released Under LGPL - original licence link has changed is not relivant.
33689  *
33690  * Fork - LGPL
33691  * <script type="text/javascript">
33692  */
33693
33694 // private
33695 // This is a support class used internally by the Grid components
33696 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
33697     this.grid = grid;
33698     this.view = grid.getView();
33699     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33700     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
33701     if(hd2){
33702         this.setHandleElId(Roo.id(hd));
33703         this.setOuterHandleElId(Roo.id(hd2));
33704     }
33705     this.scroll = false;
33706 };
33707 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
33708     maxDragWidth: 120,
33709     getDragData : function(e){
33710         var t = Roo.lib.Event.getTarget(e);
33711         var h = this.view.findHeaderCell(t);
33712         if(h){
33713             return {ddel: h.firstChild, header:h};
33714         }
33715         return false;
33716     },
33717
33718     onInitDrag : function(e){
33719         this.view.headersDisabled = true;
33720         var clone = this.dragData.ddel.cloneNode(true);
33721         clone.id = Roo.id();
33722         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
33723         this.proxy.update(clone);
33724         return true;
33725     },
33726
33727     afterValidDrop : function(){
33728         var v = this.view;
33729         setTimeout(function(){
33730             v.headersDisabled = false;
33731         }, 50);
33732     },
33733
33734     afterInvalidDrop : function(){
33735         var v = this.view;
33736         setTimeout(function(){
33737             v.headersDisabled = false;
33738         }, 50);
33739     }
33740 });
33741 /*
33742  * Based on:
33743  * Ext JS Library 1.1.1
33744  * Copyright(c) 2006-2007, Ext JS, LLC.
33745  *
33746  * Originally Released Under LGPL - original licence link has changed is not relivant.
33747  *
33748  * Fork - LGPL
33749  * <script type="text/javascript">
33750  */
33751 // private
33752 // This is a support class used internally by the Grid components
33753 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
33754     this.grid = grid;
33755     this.view = grid.getView();
33756     // split the proxies so they don't interfere with mouse events
33757     this.proxyTop = Roo.DomHelper.append(document.body, {
33758         cls:"col-move-top", html:"&#160;"
33759     }, true);
33760     this.proxyBottom = Roo.DomHelper.append(document.body, {
33761         cls:"col-move-bottom", html:"&#160;"
33762     }, true);
33763     this.proxyTop.hide = this.proxyBottom.hide = function(){
33764         this.setLeftTop(-100,-100);
33765         this.setStyle("visibility", "hidden");
33766     };
33767     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33768     // temporarily disabled
33769     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
33770     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
33771 };
33772 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
33773     proxyOffsets : [-4, -9],
33774     fly: Roo.Element.fly,
33775
33776     getTargetFromEvent : function(e){
33777         var t = Roo.lib.Event.getTarget(e);
33778         var cindex = this.view.findCellIndex(t);
33779         if(cindex !== false){
33780             return this.view.getHeaderCell(cindex);
33781         }
33782         return null;
33783     },
33784
33785     nextVisible : function(h){
33786         var v = this.view, cm = this.grid.colModel;
33787         h = h.nextSibling;
33788         while(h){
33789             if(!cm.isHidden(v.getCellIndex(h))){
33790                 return h;
33791             }
33792             h = h.nextSibling;
33793         }
33794         return null;
33795     },
33796
33797     prevVisible : function(h){
33798         var v = this.view, cm = this.grid.colModel;
33799         h = h.prevSibling;
33800         while(h){
33801             if(!cm.isHidden(v.getCellIndex(h))){
33802                 return h;
33803             }
33804             h = h.prevSibling;
33805         }
33806         return null;
33807     },
33808
33809     positionIndicator : function(h, n, e){
33810         var x = Roo.lib.Event.getPageX(e);
33811         var r = Roo.lib.Dom.getRegion(n.firstChild);
33812         var px, pt, py = r.top + this.proxyOffsets[1];
33813         if((r.right - x) <= (r.right-r.left)/2){
33814             px = r.right+this.view.borderWidth;
33815             pt = "after";
33816         }else{
33817             px = r.left;
33818             pt = "before";
33819         }
33820         var oldIndex = this.view.getCellIndex(h);
33821         var newIndex = this.view.getCellIndex(n);
33822
33823         if(this.grid.colModel.isFixed(newIndex)){
33824             return false;
33825         }
33826
33827         var locked = this.grid.colModel.isLocked(newIndex);
33828
33829         if(pt == "after"){
33830             newIndex++;
33831         }
33832         if(oldIndex < newIndex){
33833             newIndex--;
33834         }
33835         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
33836             return false;
33837         }
33838         px +=  this.proxyOffsets[0];
33839         this.proxyTop.setLeftTop(px, py);
33840         this.proxyTop.show();
33841         if(!this.bottomOffset){
33842             this.bottomOffset = this.view.mainHd.getHeight();
33843         }
33844         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
33845         this.proxyBottom.show();
33846         return pt;
33847     },
33848
33849     onNodeEnter : function(n, dd, e, data){
33850         if(data.header != n){
33851             this.positionIndicator(data.header, n, e);
33852         }
33853     },
33854
33855     onNodeOver : function(n, dd, e, data){
33856         var result = false;
33857         if(data.header != n){
33858             result = this.positionIndicator(data.header, n, e);
33859         }
33860         if(!result){
33861             this.proxyTop.hide();
33862             this.proxyBottom.hide();
33863         }
33864         return result ? this.dropAllowed : this.dropNotAllowed;
33865     },
33866
33867     onNodeOut : function(n, dd, e, data){
33868         this.proxyTop.hide();
33869         this.proxyBottom.hide();
33870     },
33871
33872     onNodeDrop : function(n, dd, e, data){
33873         var h = data.header;
33874         if(h != n){
33875             var cm = this.grid.colModel;
33876             var x = Roo.lib.Event.getPageX(e);
33877             var r = Roo.lib.Dom.getRegion(n.firstChild);
33878             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
33879             var oldIndex = this.view.getCellIndex(h);
33880             var newIndex = this.view.getCellIndex(n);
33881             var locked = cm.isLocked(newIndex);
33882             if(pt == "after"){
33883                 newIndex++;
33884             }
33885             if(oldIndex < newIndex){
33886                 newIndex--;
33887             }
33888             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
33889                 return false;
33890             }
33891             cm.setLocked(oldIndex, locked, true);
33892             cm.moveColumn(oldIndex, newIndex);
33893             this.grid.fireEvent("columnmove", oldIndex, newIndex);
33894             return true;
33895         }
33896         return false;
33897     }
33898 });
33899 /*
33900  * Based on:
33901  * Ext JS Library 1.1.1
33902  * Copyright(c) 2006-2007, Ext JS, LLC.
33903  *
33904  * Originally Released Under LGPL - original licence link has changed is not relivant.
33905  *
33906  * Fork - LGPL
33907  * <script type="text/javascript">
33908  */
33909   
33910 /**
33911  * @class Roo.grid.GridView
33912  * @extends Roo.util.Observable
33913  *
33914  * @constructor
33915  * @param {Object} config
33916  */
33917 Roo.grid.GridView = function(config){
33918     Roo.grid.GridView.superclass.constructor.call(this);
33919     this.el = null;
33920
33921     Roo.apply(this, config);
33922 };
33923
33924 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
33925
33926     /**
33927      * Override this function to apply custom css classes to rows during rendering
33928      * @param {Record} record The record
33929      * @param {Number} index
33930      * @method getRowClass
33931      */
33932     rowClass : "x-grid-row",
33933
33934     cellClass : "x-grid-col",
33935
33936     tdClass : "x-grid-td",
33937
33938     hdClass : "x-grid-hd",
33939
33940     splitClass : "x-grid-split",
33941
33942     sortClasses : ["sort-asc", "sort-desc"],
33943
33944     enableMoveAnim : false,
33945
33946     hlColor: "C3DAF9",
33947
33948     dh : Roo.DomHelper,
33949
33950     fly : Roo.Element.fly,
33951
33952     css : Roo.util.CSS,
33953
33954     borderWidth: 1,
33955
33956     splitOffset: 3,
33957
33958     scrollIncrement : 22,
33959
33960     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
33961
33962     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
33963
33964     bind : function(ds, cm){
33965         if(this.ds){
33966             this.ds.un("load", this.onLoad, this);
33967             this.ds.un("datachanged", this.onDataChange, this);
33968             this.ds.un("add", this.onAdd, this);
33969             this.ds.un("remove", this.onRemove, this);
33970             this.ds.un("update", this.onUpdate, this);
33971             this.ds.un("clear", this.onClear, this);
33972         }
33973         if(ds){
33974             ds.on("load", this.onLoad, this);
33975             ds.on("datachanged", this.onDataChange, this);
33976             ds.on("add", this.onAdd, this);
33977             ds.on("remove", this.onRemove, this);
33978             ds.on("update", this.onUpdate, this);
33979             ds.on("clear", this.onClear, this);
33980         }
33981         this.ds = ds;
33982
33983         if(this.cm){
33984             this.cm.un("widthchange", this.onColWidthChange, this);
33985             this.cm.un("headerchange", this.onHeaderChange, this);
33986             this.cm.un("hiddenchange", this.onHiddenChange, this);
33987             this.cm.un("columnmoved", this.onColumnMove, this);
33988             this.cm.un("columnlockchange", this.onColumnLock, this);
33989         }
33990         if(cm){
33991             this.generateRules(cm);
33992             cm.on("widthchange", this.onColWidthChange, this);
33993             cm.on("headerchange", this.onHeaderChange, this);
33994             cm.on("hiddenchange", this.onHiddenChange, this);
33995             cm.on("columnmoved", this.onColumnMove, this);
33996             cm.on("columnlockchange", this.onColumnLock, this);
33997         }
33998         this.cm = cm;
33999     },
34000
34001     init: function(grid){
34002         Roo.grid.GridView.superclass.init.call(this, grid);
34003
34004         this.bind(grid.dataSource, grid.colModel);
34005
34006         grid.on("headerclick", this.handleHeaderClick, this);
34007
34008         if(grid.trackMouseOver){
34009             grid.on("mouseover", this.onRowOver, this);
34010             grid.on("mouseout", this.onRowOut, this);
34011         }
34012         grid.cancelTextSelection = function(){};
34013         this.gridId = grid.id;
34014
34015         var tpls = this.templates || {};
34016
34017         if(!tpls.master){
34018             tpls.master = new Roo.Template(
34019                '<div class="x-grid" hidefocus="true">',
34020                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
34021                   '<div class="x-grid-topbar"></div>',
34022                   '<div class="x-grid-scroller"><div></div></div>',
34023                   '<div class="x-grid-locked">',
34024                       '<div class="x-grid-header">{lockedHeader}</div>',
34025                       '<div class="x-grid-body">{lockedBody}</div>',
34026                   "</div>",
34027                   '<div class="x-grid-viewport">',
34028                       '<div class="x-grid-header">{header}</div>',
34029                       '<div class="x-grid-body">{body}</div>',
34030                   "</div>",
34031                   '<div class="x-grid-bottombar"></div>',
34032                  
34033                   '<div class="x-grid-resize-proxy">&#160;</div>',
34034                "</div>"
34035             );
34036             tpls.master.disableformats = true;
34037         }
34038
34039         if(!tpls.header){
34040             tpls.header = new Roo.Template(
34041                '<table border="0" cellspacing="0" cellpadding="0">',
34042                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
34043                "</table>{splits}"
34044             );
34045             tpls.header.disableformats = true;
34046         }
34047         tpls.header.compile();
34048
34049         if(!tpls.hcell){
34050             tpls.hcell = new Roo.Template(
34051                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
34052                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
34053                 "</div></td>"
34054              );
34055              tpls.hcell.disableFormats = true;
34056         }
34057         tpls.hcell.compile();
34058
34059         if(!tpls.hsplit){
34060             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
34061             tpls.hsplit.disableFormats = true;
34062         }
34063         tpls.hsplit.compile();
34064
34065         if(!tpls.body){
34066             tpls.body = new Roo.Template(
34067                '<table border="0" cellspacing="0" cellpadding="0">',
34068                "<tbody>{rows}</tbody>",
34069                "</table>"
34070             );
34071             tpls.body.disableFormats = true;
34072         }
34073         tpls.body.compile();
34074
34075         if(!tpls.row){
34076             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
34077             tpls.row.disableFormats = true;
34078         }
34079         tpls.row.compile();
34080
34081         if(!tpls.cell){
34082             tpls.cell = new Roo.Template(
34083                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
34084                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
34085                 "</td>"
34086             );
34087             tpls.cell.disableFormats = true;
34088         }
34089         tpls.cell.compile();
34090
34091         this.templates = tpls;
34092     },
34093
34094     // remap these for backwards compat
34095     onColWidthChange : function(){
34096         this.updateColumns.apply(this, arguments);
34097     },
34098     onHeaderChange : function(){
34099         this.updateHeaders.apply(this, arguments);
34100     }, 
34101     onHiddenChange : function(){
34102         this.handleHiddenChange.apply(this, arguments);
34103     },
34104     onColumnMove : function(){
34105         this.handleColumnMove.apply(this, arguments);
34106     },
34107     onColumnLock : function(){
34108         this.handleLockChange.apply(this, arguments);
34109     },
34110
34111     onDataChange : function(){
34112         this.refresh();
34113         this.updateHeaderSortState();
34114     },
34115
34116     onClear : function(){
34117         this.refresh();
34118     },
34119
34120     onUpdate : function(ds, record){
34121         this.refreshRow(record);
34122     },
34123
34124     refreshRow : function(record){
34125         var ds = this.ds, index;
34126         if(typeof record == 'number'){
34127             index = record;
34128             record = ds.getAt(index);
34129         }else{
34130             index = ds.indexOf(record);
34131         }
34132         this.insertRows(ds, index, index, true);
34133         this.onRemove(ds, record, index+1, true);
34134         this.syncRowHeights(index, index);
34135         this.layout();
34136         this.fireEvent("rowupdated", this, index, record);
34137     },
34138
34139     onAdd : function(ds, records, index){
34140         this.insertRows(ds, index, index + (records.length-1));
34141     },
34142
34143     onRemove : function(ds, record, index, isUpdate){
34144         if(isUpdate !== true){
34145             this.fireEvent("beforerowremoved", this, index, record);
34146         }
34147         var bt = this.getBodyTable(), lt = this.getLockedTable();
34148         if(bt.rows[index]){
34149             bt.firstChild.removeChild(bt.rows[index]);
34150         }
34151         if(lt.rows[index]){
34152             lt.firstChild.removeChild(lt.rows[index]);
34153         }
34154         if(isUpdate !== true){
34155             this.stripeRows(index);
34156             this.syncRowHeights(index, index);
34157             this.layout();
34158             this.fireEvent("rowremoved", this, index, record);
34159         }
34160     },
34161
34162     onLoad : function(){
34163         this.scrollToTop();
34164     },
34165
34166     /**
34167      * Scrolls the grid to the top
34168      */
34169     scrollToTop : function(){
34170         if(this.scroller){
34171             this.scroller.dom.scrollTop = 0;
34172             this.syncScroll();
34173         }
34174     },
34175
34176     /**
34177      * Gets a panel in the header of the grid that can be used for toolbars etc.
34178      * After modifying the contents of this panel a call to grid.autoSize() may be
34179      * required to register any changes in size.
34180      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
34181      * @return Roo.Element
34182      */
34183     getHeaderPanel : function(doShow){
34184         if(doShow){
34185             this.headerPanel.show();
34186         }
34187         return this.headerPanel;
34188     },
34189
34190     /**
34191      * Gets a panel in the footer of the grid that can be used for toolbars etc.
34192      * After modifying the contents of this panel a call to grid.autoSize() may be
34193      * required to register any changes in size.
34194      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
34195      * @return Roo.Element
34196      */
34197     getFooterPanel : function(doShow){
34198         if(doShow){
34199             this.footerPanel.show();
34200         }
34201         return this.footerPanel;
34202     },
34203
34204     initElements : function(){
34205         var E = Roo.Element;
34206         var el = this.grid.getGridEl().dom.firstChild;
34207         var cs = el.childNodes;
34208
34209         this.el = new E(el);
34210         
34211          this.focusEl = new E(el.firstChild);
34212         this.focusEl.swallowEvent("click", true);
34213         
34214         this.headerPanel = new E(cs[1]);
34215         this.headerPanel.enableDisplayMode("block");
34216
34217         this.scroller = new E(cs[2]);
34218         this.scrollSizer = new E(this.scroller.dom.firstChild);
34219
34220         this.lockedWrap = new E(cs[3]);
34221         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
34222         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
34223
34224         this.mainWrap = new E(cs[4]);
34225         this.mainHd = new E(this.mainWrap.dom.firstChild);
34226         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
34227
34228         this.footerPanel = new E(cs[5]);
34229         this.footerPanel.enableDisplayMode("block");
34230
34231         this.resizeProxy = new E(cs[6]);
34232
34233         this.headerSelector = String.format(
34234            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
34235            this.lockedHd.id, this.mainHd.id
34236         );
34237
34238         this.splitterSelector = String.format(
34239            '#{0} div.x-grid-split, #{1} div.x-grid-split',
34240            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
34241         );
34242     },
34243     idToCssName : function(s)
34244     {
34245         return s.replace(/[^a-z0-9]+/ig, '-');
34246     },
34247
34248     getHeaderCell : function(index){
34249         return Roo.DomQuery.select(this.headerSelector)[index];
34250     },
34251
34252     getHeaderCellMeasure : function(index){
34253         return this.getHeaderCell(index).firstChild;
34254     },
34255
34256     getHeaderCellText : function(index){
34257         return this.getHeaderCell(index).firstChild.firstChild;
34258     },
34259
34260     getLockedTable : function(){
34261         return this.lockedBody.dom.firstChild;
34262     },
34263
34264     getBodyTable : function(){
34265         return this.mainBody.dom.firstChild;
34266     },
34267
34268     getLockedRow : function(index){
34269         return this.getLockedTable().rows[index];
34270     },
34271
34272     getRow : function(index){
34273         return this.getBodyTable().rows[index];
34274     },
34275
34276     getRowComposite : function(index){
34277         if(!this.rowEl){
34278             this.rowEl = new Roo.CompositeElementLite();
34279         }
34280         var els = [], lrow, mrow;
34281         if(lrow = this.getLockedRow(index)){
34282             els.push(lrow);
34283         }
34284         if(mrow = this.getRow(index)){
34285             els.push(mrow);
34286         }
34287         this.rowEl.elements = els;
34288         return this.rowEl;
34289     },
34290     /**
34291      * Gets the 'td' of the cell
34292      * 
34293      * @param {Integer} rowIndex row to select
34294      * @param {Integer} colIndex column to select
34295      * 
34296      * @return {Object} 
34297      */
34298     getCell : function(rowIndex, colIndex){
34299         var locked = this.cm.getLockedCount();
34300         var source;
34301         if(colIndex < locked){
34302             source = this.lockedBody.dom.firstChild;
34303         }else{
34304             source = this.mainBody.dom.firstChild;
34305             colIndex -= locked;
34306         }
34307         return source.rows[rowIndex].childNodes[colIndex];
34308     },
34309
34310     getCellText : function(rowIndex, colIndex){
34311         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
34312     },
34313
34314     getCellBox : function(cell){
34315         var b = this.fly(cell).getBox();
34316         if(Roo.isOpera){ // opera fails to report the Y
34317             b.y = cell.offsetTop + this.mainBody.getY();
34318         }
34319         return b;
34320     },
34321
34322     getCellIndex : function(cell){
34323         var id = String(cell.className).match(this.cellRE);
34324         if(id){
34325             return parseInt(id[1], 10);
34326         }
34327         return 0;
34328     },
34329
34330     findHeaderIndex : function(n){
34331         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
34332         return r ? this.getCellIndex(r) : false;
34333     },
34334
34335     findHeaderCell : function(n){
34336         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
34337         return r ? r : false;
34338     },
34339
34340     findRowIndex : function(n){
34341         if(!n){
34342             return false;
34343         }
34344         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
34345         return r ? r.rowIndex : false;
34346     },
34347
34348     findCellIndex : function(node){
34349         var stop = this.el.dom;
34350         while(node && node != stop){
34351             if(this.findRE.test(node.className)){
34352                 return this.getCellIndex(node);
34353             }
34354             node = node.parentNode;
34355         }
34356         return false;
34357     },
34358
34359     getColumnId : function(index){
34360         return this.cm.getColumnId(index);
34361     },
34362
34363     getSplitters : function()
34364     {
34365         if(this.splitterSelector){
34366            return Roo.DomQuery.select(this.splitterSelector);
34367         }else{
34368             return null;
34369       }
34370     },
34371
34372     getSplitter : function(index){
34373         return this.getSplitters()[index];
34374     },
34375
34376     onRowOver : function(e, t){
34377         var row;
34378         if((row = this.findRowIndex(t)) !== false){
34379             this.getRowComposite(row).addClass("x-grid-row-over");
34380         }
34381     },
34382
34383     onRowOut : function(e, t){
34384         var row;
34385         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
34386             this.getRowComposite(row).removeClass("x-grid-row-over");
34387         }
34388     },
34389
34390     renderHeaders : function(){
34391         var cm = this.cm;
34392         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
34393         var cb = [], lb = [], sb = [], lsb = [], p = {};
34394         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34395             p.cellId = "x-grid-hd-0-" + i;
34396             p.splitId = "x-grid-csplit-0-" + i;
34397             p.id = cm.getColumnId(i);
34398             p.title = cm.getColumnTooltip(i) || "";
34399             p.value = cm.getColumnHeader(i) || "";
34400             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
34401             if(!cm.isLocked(i)){
34402                 cb[cb.length] = ct.apply(p);
34403                 sb[sb.length] = st.apply(p);
34404             }else{
34405                 lb[lb.length] = ct.apply(p);
34406                 lsb[lsb.length] = st.apply(p);
34407             }
34408         }
34409         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
34410                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
34411     },
34412
34413     updateHeaders : function(){
34414         var html = this.renderHeaders();
34415         this.lockedHd.update(html[0]);
34416         this.mainHd.update(html[1]);
34417     },
34418
34419     /**
34420      * Focuses the specified row.
34421      * @param {Number} row The row index
34422      */
34423     focusRow : function(row)
34424     {
34425         //Roo.log('GridView.focusRow');
34426         var x = this.scroller.dom.scrollLeft;
34427         this.focusCell(row, 0, false);
34428         this.scroller.dom.scrollLeft = x;
34429     },
34430
34431     /**
34432      * Focuses the specified cell.
34433      * @param {Number} row The row index
34434      * @param {Number} col The column index
34435      * @param {Boolean} hscroll false to disable horizontal scrolling
34436      */
34437     focusCell : function(row, col, hscroll)
34438     {
34439         //Roo.log('GridView.focusCell');
34440         var el = this.ensureVisible(row, col, hscroll);
34441         this.focusEl.alignTo(el, "tl-tl");
34442         if(Roo.isGecko){
34443             this.focusEl.focus();
34444         }else{
34445             this.focusEl.focus.defer(1, this.focusEl);
34446         }
34447     },
34448
34449     /**
34450      * Scrolls the specified cell into view
34451      * @param {Number} row The row index
34452      * @param {Number} col The column index
34453      * @param {Boolean} hscroll false to disable horizontal scrolling
34454      */
34455     ensureVisible : function(row, col, hscroll)
34456     {
34457         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
34458         //return null; //disable for testing.
34459         if(typeof row != "number"){
34460             row = row.rowIndex;
34461         }
34462         if(row < 0 && row >= this.ds.getCount()){
34463             return  null;
34464         }
34465         col = (col !== undefined ? col : 0);
34466         var cm = this.grid.colModel;
34467         while(cm.isHidden(col)){
34468             col++;
34469         }
34470
34471         var el = this.getCell(row, col);
34472         if(!el){
34473             return null;
34474         }
34475         var c = this.scroller.dom;
34476
34477         var ctop = parseInt(el.offsetTop, 10);
34478         var cleft = parseInt(el.offsetLeft, 10);
34479         var cbot = ctop + el.offsetHeight;
34480         var cright = cleft + el.offsetWidth;
34481         
34482         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
34483         var stop = parseInt(c.scrollTop, 10);
34484         var sleft = parseInt(c.scrollLeft, 10);
34485         var sbot = stop + ch;
34486         var sright = sleft + c.clientWidth;
34487         /*
34488         Roo.log('GridView.ensureVisible:' +
34489                 ' ctop:' + ctop +
34490                 ' c.clientHeight:' + c.clientHeight +
34491                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
34492                 ' stop:' + stop +
34493                 ' cbot:' + cbot +
34494                 ' sbot:' + sbot +
34495                 ' ch:' + ch  
34496                 );
34497         */
34498         if(ctop < stop){
34499              c.scrollTop = ctop;
34500             //Roo.log("set scrolltop to ctop DISABLE?");
34501         }else if(cbot > sbot){
34502             //Roo.log("set scrolltop to cbot-ch");
34503             c.scrollTop = cbot-ch;
34504         }
34505         
34506         if(hscroll !== false){
34507             if(cleft < sleft){
34508                 c.scrollLeft = cleft;
34509             }else if(cright > sright){
34510                 c.scrollLeft = cright-c.clientWidth;
34511             }
34512         }
34513          
34514         return el;
34515     },
34516
34517     updateColumns : function(){
34518         this.grid.stopEditing();
34519         var cm = this.grid.colModel, colIds = this.getColumnIds();
34520         //var totalWidth = cm.getTotalWidth();
34521         var pos = 0;
34522         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34523             //if(cm.isHidden(i)) continue;
34524             var w = cm.getColumnWidth(i);
34525             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
34526             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
34527         }
34528         this.updateSplitters();
34529     },
34530
34531     generateRules : function(cm){
34532         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
34533         Roo.util.CSS.removeStyleSheet(rulesId);
34534         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34535             var cid = cm.getColumnId(i);
34536             var align = '';
34537             if(cm.config[i].align){
34538                 align = 'text-align:'+cm.config[i].align+';';
34539             }
34540             var hidden = '';
34541             if(cm.isHidden(i)){
34542                 hidden = 'display:none;';
34543             }
34544             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
34545             ruleBuf.push(
34546                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
34547                     this.hdSelector, cid, " {\n", align, width, "}\n",
34548                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
34549                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
34550         }
34551         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
34552     },
34553
34554     updateSplitters : function(){
34555         var cm = this.cm, s = this.getSplitters();
34556         if(s){ // splitters not created yet
34557             var pos = 0, locked = true;
34558             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34559                 if(cm.isHidden(i)) continue;
34560                 var w = cm.getColumnWidth(i); // make sure it's a number
34561                 if(!cm.isLocked(i) && locked){
34562                     pos = 0;
34563                     locked = false;
34564                 }
34565                 pos += w;
34566                 s[i].style.left = (pos-this.splitOffset) + "px";
34567             }
34568         }
34569     },
34570
34571     handleHiddenChange : function(colModel, colIndex, hidden){
34572         if(hidden){
34573             this.hideColumn(colIndex);
34574         }else{
34575             this.unhideColumn(colIndex);
34576         }
34577     },
34578
34579     hideColumn : function(colIndex){
34580         var cid = this.getColumnId(colIndex);
34581         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
34582         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
34583         if(Roo.isSafari){
34584             this.updateHeaders();
34585         }
34586         this.updateSplitters();
34587         this.layout();
34588     },
34589
34590     unhideColumn : function(colIndex){
34591         var cid = this.getColumnId(colIndex);
34592         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
34593         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
34594
34595         if(Roo.isSafari){
34596             this.updateHeaders();
34597         }
34598         this.updateSplitters();
34599         this.layout();
34600     },
34601
34602     insertRows : function(dm, firstRow, lastRow, isUpdate){
34603         if(firstRow == 0 && lastRow == dm.getCount()-1){
34604             this.refresh();
34605         }else{
34606             if(!isUpdate){
34607                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
34608             }
34609             var s = this.getScrollState();
34610             var markup = this.renderRows(firstRow, lastRow);
34611             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
34612             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
34613             this.restoreScroll(s);
34614             if(!isUpdate){
34615                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
34616                 this.syncRowHeights(firstRow, lastRow);
34617                 this.stripeRows(firstRow);
34618                 this.layout();
34619             }
34620         }
34621     },
34622
34623     bufferRows : function(markup, target, index){
34624         var before = null, trows = target.rows, tbody = target.tBodies[0];
34625         if(index < trows.length){
34626             before = trows[index];
34627         }
34628         var b = document.createElement("div");
34629         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
34630         var rows = b.firstChild.rows;
34631         for(var i = 0, len = rows.length; i < len; i++){
34632             if(before){
34633                 tbody.insertBefore(rows[0], before);
34634             }else{
34635                 tbody.appendChild(rows[0]);
34636             }
34637         }
34638         b.innerHTML = "";
34639         b = null;
34640     },
34641
34642     deleteRows : function(dm, firstRow, lastRow){
34643         if(dm.getRowCount()<1){
34644             this.fireEvent("beforerefresh", this);
34645             this.mainBody.update("");
34646             this.lockedBody.update("");
34647             this.fireEvent("refresh", this);
34648         }else{
34649             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
34650             var bt = this.getBodyTable();
34651             var tbody = bt.firstChild;
34652             var rows = bt.rows;
34653             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
34654                 tbody.removeChild(rows[firstRow]);
34655             }
34656             this.stripeRows(firstRow);
34657             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
34658         }
34659     },
34660
34661     updateRows : function(dataSource, firstRow, lastRow){
34662         var s = this.getScrollState();
34663         this.refresh();
34664         this.restoreScroll(s);
34665     },
34666
34667     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
34668         if(!noRefresh){
34669            this.refresh();
34670         }
34671         this.updateHeaderSortState();
34672     },
34673
34674     getScrollState : function(){
34675         
34676         var sb = this.scroller.dom;
34677         return {left: sb.scrollLeft, top: sb.scrollTop};
34678     },
34679
34680     stripeRows : function(startRow){
34681         if(!this.grid.stripeRows || this.ds.getCount() < 1){
34682             return;
34683         }
34684         startRow = startRow || 0;
34685         var rows = this.getBodyTable().rows;
34686         var lrows = this.getLockedTable().rows;
34687         var cls = ' x-grid-row-alt ';
34688         for(var i = startRow, len = rows.length; i < len; i++){
34689             var row = rows[i], lrow = lrows[i];
34690             var isAlt = ((i+1) % 2 == 0);
34691             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
34692             if(isAlt == hasAlt){
34693                 continue;
34694             }
34695             if(isAlt){
34696                 row.className += " x-grid-row-alt";
34697             }else{
34698                 row.className = row.className.replace("x-grid-row-alt", "");
34699             }
34700             if(lrow){
34701                 lrow.className = row.className;
34702             }
34703         }
34704     },
34705
34706     restoreScroll : function(state){
34707         //Roo.log('GridView.restoreScroll');
34708         var sb = this.scroller.dom;
34709         sb.scrollLeft = state.left;
34710         sb.scrollTop = state.top;
34711         this.syncScroll();
34712     },
34713
34714     syncScroll : function(){
34715         //Roo.log('GridView.syncScroll');
34716         var sb = this.scroller.dom;
34717         var sh = this.mainHd.dom;
34718         var bs = this.mainBody.dom;
34719         var lv = this.lockedBody.dom;
34720         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
34721         lv.scrollTop = bs.scrollTop = sb.scrollTop;
34722     },
34723
34724     handleScroll : function(e){
34725         this.syncScroll();
34726         var sb = this.scroller.dom;
34727         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
34728         e.stopEvent();
34729     },
34730
34731     handleWheel : function(e){
34732         var d = e.getWheelDelta();
34733         this.scroller.dom.scrollTop -= d*22;
34734         // set this here to prevent jumpy scrolling on large tables
34735         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
34736         e.stopEvent();
34737     },
34738
34739     renderRows : function(startRow, endRow){
34740         // pull in all the crap needed to render rows
34741         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
34742         var colCount = cm.getColumnCount();
34743
34744         if(ds.getCount() < 1){
34745             return ["", ""];
34746         }
34747
34748         // build a map for all the columns
34749         var cs = [];
34750         for(var i = 0; i < colCount; i++){
34751             var name = cm.getDataIndex(i);
34752             cs[i] = {
34753                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
34754                 renderer : cm.getRenderer(i),
34755                 id : cm.getColumnId(i),
34756                 locked : cm.isLocked(i)
34757             };
34758         }
34759
34760         startRow = startRow || 0;
34761         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
34762
34763         // records to render
34764         var rs = ds.getRange(startRow, endRow);
34765
34766         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
34767     },
34768
34769     // As much as I hate to duplicate code, this was branched because FireFox really hates
34770     // [].join("") on strings. The performance difference was substantial enough to
34771     // branch this function
34772     doRender : Roo.isGecko ?
34773             function(cs, rs, ds, startRow, colCount, stripe){
34774                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34775                 // buffers
34776                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34777                 
34778                 var hasListener = this.grid.hasListener('rowclass');
34779                 var rowcfg = {};
34780                 for(var j = 0, len = rs.length; j < len; j++){
34781                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
34782                     for(var i = 0; i < colCount; i++){
34783                         c = cs[i];
34784                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34785                         p.id = c.id;
34786                         p.css = p.attr = "";
34787                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34788                         if(p.value == undefined || p.value === "") p.value = "&#160;";
34789                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
34790                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
34791                         }
34792                         var markup = ct.apply(p);
34793                         if(!c.locked){
34794                             cb+= markup;
34795                         }else{
34796                             lcb+= markup;
34797                         }
34798                     }
34799                     var alt = [];
34800                     if(stripe && ((rowIndex+1) % 2 == 0)){
34801                         alt.push("x-grid-row-alt")
34802                     }
34803                     if(r.dirty){
34804                         alt.push(  " x-grid-dirty-row");
34805                     }
34806                     rp.cells = lcb;
34807                     if(this.getRowClass){
34808                         alt.push(this.getRowClass(r, rowIndex));
34809                     }
34810                     if (hasListener) {
34811                         rowcfg = {
34812                              
34813                             record: r,
34814                             rowIndex : rowIndex,
34815                             rowClass : ''
34816                         }
34817                         this.grid.fireEvent('rowclass', this, rowcfg);
34818                         alt.push(rowcfg.rowClass);
34819                     }
34820                     rp.alt = alt.join(" ");
34821                     lbuf+= rt.apply(rp);
34822                     rp.cells = cb;
34823                     buf+=  rt.apply(rp);
34824                 }
34825                 return [lbuf, buf];
34826             } :
34827             function(cs, rs, ds, startRow, colCount, stripe){
34828                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34829                 // buffers
34830                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34831                 var hasListener = this.grid.hasListener('rowclass');
34832                 var rowcfg = {};
34833                 for(var j = 0, len = rs.length; j < len; j++){
34834                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
34835                     for(var i = 0; i < colCount; i++){
34836                         c = cs[i];
34837                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34838                         p.id = c.id;
34839                         p.css = p.attr = "";
34840                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34841                         if(p.value == undefined || p.value === "") p.value = "&#160;";
34842                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
34843                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
34844                         }
34845                         var markup = ct.apply(p);
34846                         if(!c.locked){
34847                             cb[cb.length] = markup;
34848                         }else{
34849                             lcb[lcb.length] = markup;
34850                         }
34851                     }
34852                     var alt = [];
34853                     if(stripe && ((rowIndex+1) % 2 == 0)){
34854                         alt.push( "x-grid-row-alt");
34855                     }
34856                     if(r.dirty){
34857                         alt.push(" x-grid-dirty-row");
34858                     }
34859                     rp.cells = lcb;
34860                     if(this.getRowClass){
34861                         alt.push( this.getRowClass(r, rowIndex));
34862                     }
34863                     if (hasListener) {
34864                         rowcfg = {
34865                              
34866                             record: r,
34867                             rowIndex : rowIndex,
34868                             rowClass : ''
34869                         }
34870                         this.grid.fireEvent('rowclass', this, rowcfg);
34871                         alt.push(rowcfg.rowClass);
34872                     }
34873                     rp.alt = alt.join(" ");
34874                     rp.cells = lcb.join("");
34875                     lbuf[lbuf.length] = rt.apply(rp);
34876                     rp.cells = cb.join("");
34877                     buf[buf.length] =  rt.apply(rp);
34878                 }
34879                 return [lbuf.join(""), buf.join("")];
34880             },
34881
34882     renderBody : function(){
34883         var markup = this.renderRows();
34884         var bt = this.templates.body;
34885         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
34886     },
34887
34888     /**
34889      * Refreshes the grid
34890      * @param {Boolean} headersToo
34891      */
34892     refresh : function(headersToo){
34893         this.fireEvent("beforerefresh", this);
34894         this.grid.stopEditing();
34895         var result = this.renderBody();
34896         this.lockedBody.update(result[0]);
34897         this.mainBody.update(result[1]);
34898         if(headersToo === true){
34899             this.updateHeaders();
34900             this.updateColumns();
34901             this.updateSplitters();
34902             this.updateHeaderSortState();
34903         }
34904         this.syncRowHeights();
34905         this.layout();
34906         this.fireEvent("refresh", this);
34907     },
34908
34909     handleColumnMove : function(cm, oldIndex, newIndex){
34910         this.indexMap = null;
34911         var s = this.getScrollState();
34912         this.refresh(true);
34913         this.restoreScroll(s);
34914         this.afterMove(newIndex);
34915     },
34916
34917     afterMove : function(colIndex){
34918         if(this.enableMoveAnim && Roo.enableFx){
34919             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
34920         }
34921         // if multisort - fix sortOrder, and reload..
34922         if (this.grid.dataSource.multiSort) {
34923             // the we can call sort again..
34924             var dm = this.grid.dataSource;
34925             var cm = this.grid.colModel;
34926             var so = [];
34927             for(var i = 0; i < cm.config.length; i++ ) {
34928                 
34929                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
34930                     continue; // dont' bother, it's not in sort list or being set.
34931                 }
34932                 
34933                 so.push(cm.config[i].dataIndex);
34934             };
34935             dm.sortOrder = so;
34936             dm.load(dm.lastOptions);
34937             
34938             
34939         }
34940         
34941     },
34942
34943     updateCell : function(dm, rowIndex, dataIndex){
34944         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
34945         if(typeof colIndex == "undefined"){ // not present in grid
34946             return;
34947         }
34948         var cm = this.grid.colModel;
34949         var cell = this.getCell(rowIndex, colIndex);
34950         var cellText = this.getCellText(rowIndex, colIndex);
34951
34952         var p = {
34953             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
34954             id : cm.getColumnId(colIndex),
34955             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
34956         };
34957         var renderer = cm.getRenderer(colIndex);
34958         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
34959         if(typeof val == "undefined" || val === "") val = "&#160;";
34960         cellText.innerHTML = val;
34961         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
34962         this.syncRowHeights(rowIndex, rowIndex);
34963     },
34964
34965     calcColumnWidth : function(colIndex, maxRowsToMeasure){
34966         var maxWidth = 0;
34967         if(this.grid.autoSizeHeaders){
34968             var h = this.getHeaderCellMeasure(colIndex);
34969             maxWidth = Math.max(maxWidth, h.scrollWidth);
34970         }
34971         var tb, index;
34972         if(this.cm.isLocked(colIndex)){
34973             tb = this.getLockedTable();
34974             index = colIndex;
34975         }else{
34976             tb = this.getBodyTable();
34977             index = colIndex - this.cm.getLockedCount();
34978         }
34979         if(tb && tb.rows){
34980             var rows = tb.rows;
34981             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
34982             for(var i = 0; i < stopIndex; i++){
34983                 var cell = rows[i].childNodes[index].firstChild;
34984                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
34985             }
34986         }
34987         return maxWidth + /*margin for error in IE*/ 5;
34988     },
34989     /**
34990      * Autofit a column to its content.
34991      * @param {Number} colIndex
34992      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
34993      */
34994      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
34995          if(this.cm.isHidden(colIndex)){
34996              return; // can't calc a hidden column
34997          }
34998         if(forceMinSize){
34999             var cid = this.cm.getColumnId(colIndex);
35000             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
35001            if(this.grid.autoSizeHeaders){
35002                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
35003            }
35004         }
35005         var newWidth = this.calcColumnWidth(colIndex);
35006         this.cm.setColumnWidth(colIndex,
35007             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
35008         if(!suppressEvent){
35009             this.grid.fireEvent("columnresize", colIndex, newWidth);
35010         }
35011     },
35012
35013     /**
35014      * Autofits all columns to their content and then expands to fit any extra space in the grid
35015      */
35016      autoSizeColumns : function(){
35017         var cm = this.grid.colModel;
35018         var colCount = cm.getColumnCount();
35019         for(var i = 0; i < colCount; i++){
35020             this.autoSizeColumn(i, true, true);
35021         }
35022         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
35023             this.fitColumns();
35024         }else{
35025             this.updateColumns();
35026             this.layout();
35027         }
35028     },
35029
35030     /**
35031      * Autofits all columns to the grid's width proportionate with their current size
35032      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
35033      */
35034     fitColumns : function(reserveScrollSpace){
35035         var cm = this.grid.colModel;
35036         var colCount = cm.getColumnCount();
35037         var cols = [];
35038         var width = 0;
35039         var i, w;
35040         for (i = 0; i < colCount; i++){
35041             if(!cm.isHidden(i) && !cm.isFixed(i)){
35042                 w = cm.getColumnWidth(i);
35043                 cols.push(i);
35044                 cols.push(w);
35045                 width += w;
35046             }
35047         }
35048         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
35049         if(reserveScrollSpace){
35050             avail -= 17;
35051         }
35052         var frac = (avail - cm.getTotalWidth())/width;
35053         while (cols.length){
35054             w = cols.pop();
35055             i = cols.pop();
35056             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
35057         }
35058         this.updateColumns();
35059         this.layout();
35060     },
35061
35062     onRowSelect : function(rowIndex){
35063         var row = this.getRowComposite(rowIndex);
35064         row.addClass("x-grid-row-selected");
35065     },
35066
35067     onRowDeselect : function(rowIndex){
35068         var row = this.getRowComposite(rowIndex);
35069         row.removeClass("x-grid-row-selected");
35070     },
35071
35072     onCellSelect : function(row, col){
35073         var cell = this.getCell(row, col);
35074         if(cell){
35075             Roo.fly(cell).addClass("x-grid-cell-selected");
35076         }
35077     },
35078
35079     onCellDeselect : function(row, col){
35080         var cell = this.getCell(row, col);
35081         if(cell){
35082             Roo.fly(cell).removeClass("x-grid-cell-selected");
35083         }
35084     },
35085
35086     updateHeaderSortState : function(){
35087         
35088         // sort state can be single { field: xxx, direction : yyy}
35089         // or   { xxx=>ASC , yyy : DESC ..... }
35090         
35091         var mstate = {};
35092         if (!this.ds.multiSort) { 
35093             var state = this.ds.getSortState();
35094             if(!state){
35095                 return;
35096             }
35097             mstate[state.field] = state.direction;
35098             // FIXME... - this is not used here.. but might be elsewhere..
35099             this.sortState = state;
35100             
35101         } else {
35102             mstate = this.ds.sortToggle;
35103         }
35104         //remove existing sort classes..
35105         
35106         var sc = this.sortClasses;
35107         var hds = this.el.select(this.headerSelector).removeClass(sc);
35108         
35109         for(var f in mstate) {
35110         
35111             var sortColumn = this.cm.findColumnIndex(f);
35112             
35113             if(sortColumn != -1){
35114                 var sortDir = mstate[f];        
35115                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
35116             }
35117         }
35118         
35119          
35120         
35121     },
35122
35123
35124     handleHeaderClick : function(g, index){
35125         if(this.headersDisabled){
35126             return;
35127         }
35128         var dm = g.dataSource, cm = g.colModel;
35129         if(!cm.isSortable(index)){
35130             return;
35131         }
35132         g.stopEditing();
35133         
35134         if (dm.multiSort) {
35135             // update the sortOrder
35136             var so = [];
35137             for(var i = 0; i < cm.config.length; i++ ) {
35138                 
35139                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
35140                     continue; // dont' bother, it's not in sort list or being set.
35141                 }
35142                 
35143                 so.push(cm.config[i].dataIndex);
35144             };
35145             dm.sortOrder = so;
35146         }
35147         
35148         
35149         dm.sort(cm.getDataIndex(index));
35150     },
35151
35152
35153     destroy : function(){
35154         if(this.colMenu){
35155             this.colMenu.removeAll();
35156             Roo.menu.MenuMgr.unregister(this.colMenu);
35157             this.colMenu.getEl().remove();
35158             delete this.colMenu;
35159         }
35160         if(this.hmenu){
35161             this.hmenu.removeAll();
35162             Roo.menu.MenuMgr.unregister(this.hmenu);
35163             this.hmenu.getEl().remove();
35164             delete this.hmenu;
35165         }
35166         if(this.grid.enableColumnMove){
35167             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
35168             if(dds){
35169                 for(var dd in dds){
35170                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
35171                         var elid = dds[dd].dragElId;
35172                         dds[dd].unreg();
35173                         Roo.get(elid).remove();
35174                     } else if(dds[dd].config.isTarget){
35175                         dds[dd].proxyTop.remove();
35176                         dds[dd].proxyBottom.remove();
35177                         dds[dd].unreg();
35178                     }
35179                     if(Roo.dd.DDM.locationCache[dd]){
35180                         delete Roo.dd.DDM.locationCache[dd];
35181                     }
35182                 }
35183                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
35184             }
35185         }
35186         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
35187         this.bind(null, null);
35188         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
35189     },
35190
35191     handleLockChange : function(){
35192         this.refresh(true);
35193     },
35194
35195     onDenyColumnLock : function(){
35196
35197     },
35198
35199     onDenyColumnHide : function(){
35200
35201     },
35202
35203     handleHdMenuClick : function(item){
35204         var index = this.hdCtxIndex;
35205         var cm = this.cm, ds = this.ds;
35206         switch(item.id){
35207             case "asc":
35208                 ds.sort(cm.getDataIndex(index), "ASC");
35209                 break;
35210             case "desc":
35211                 ds.sort(cm.getDataIndex(index), "DESC");
35212                 break;
35213             case "lock":
35214                 var lc = cm.getLockedCount();
35215                 if(cm.getColumnCount(true) <= lc+1){
35216                     this.onDenyColumnLock();
35217                     return;
35218                 }
35219                 if(lc != index){
35220                     cm.setLocked(index, true, true);
35221                     cm.moveColumn(index, lc);
35222                     this.grid.fireEvent("columnmove", index, lc);
35223                 }else{
35224                     cm.setLocked(index, true);
35225                 }
35226             break;
35227             case "unlock":
35228                 var lc = cm.getLockedCount();
35229                 if((lc-1) != index){
35230                     cm.setLocked(index, false, true);
35231                     cm.moveColumn(index, lc-1);
35232                     this.grid.fireEvent("columnmove", index, lc-1);
35233                 }else{
35234                     cm.setLocked(index, false);
35235                 }
35236             break;
35237             default:
35238                 index = cm.getIndexById(item.id.substr(4));
35239                 if(index != -1){
35240                     if(item.checked && cm.getColumnCount(true) <= 1){
35241                         this.onDenyColumnHide();
35242                         return false;
35243                     }
35244                     cm.setHidden(index, item.checked);
35245                 }
35246         }
35247         return true;
35248     },
35249
35250     beforeColMenuShow : function(){
35251         var cm = this.cm,  colCount = cm.getColumnCount();
35252         this.colMenu.removeAll();
35253         for(var i = 0; i < colCount; i++){
35254             this.colMenu.add(new Roo.menu.CheckItem({
35255                 id: "col-"+cm.getColumnId(i),
35256                 text: cm.getColumnHeader(i),
35257                 checked: !cm.isHidden(i),
35258                 hideOnClick:false
35259             }));
35260         }
35261     },
35262
35263     handleHdCtx : function(g, index, e){
35264         e.stopEvent();
35265         var hd = this.getHeaderCell(index);
35266         this.hdCtxIndex = index;
35267         var ms = this.hmenu.items, cm = this.cm;
35268         ms.get("asc").setDisabled(!cm.isSortable(index));
35269         ms.get("desc").setDisabled(!cm.isSortable(index));
35270         if(this.grid.enableColLock !== false){
35271             ms.get("lock").setDisabled(cm.isLocked(index));
35272             ms.get("unlock").setDisabled(!cm.isLocked(index));
35273         }
35274         this.hmenu.show(hd, "tl-bl");
35275     },
35276
35277     handleHdOver : function(e){
35278         var hd = this.findHeaderCell(e.getTarget());
35279         if(hd && !this.headersDisabled){
35280             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
35281                this.fly(hd).addClass("x-grid-hd-over");
35282             }
35283         }
35284     },
35285
35286     handleHdOut : function(e){
35287         var hd = this.findHeaderCell(e.getTarget());
35288         if(hd){
35289             this.fly(hd).removeClass("x-grid-hd-over");
35290         }
35291     },
35292
35293     handleSplitDblClick : function(e, t){
35294         var i = this.getCellIndex(t);
35295         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
35296             this.autoSizeColumn(i, true);
35297             this.layout();
35298         }
35299     },
35300
35301     render : function(){
35302
35303         var cm = this.cm;
35304         var colCount = cm.getColumnCount();
35305
35306         if(this.grid.monitorWindowResize === true){
35307             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35308         }
35309         var header = this.renderHeaders();
35310         var body = this.templates.body.apply({rows:""});
35311         var html = this.templates.master.apply({
35312             lockedBody: body,
35313             body: body,
35314             lockedHeader: header[0],
35315             header: header[1]
35316         });
35317
35318         //this.updateColumns();
35319
35320         this.grid.getGridEl().dom.innerHTML = html;
35321
35322         this.initElements();
35323         
35324         // a kludge to fix the random scolling effect in webkit
35325         this.el.on("scroll", function() {
35326             this.el.dom.scrollTop=0; // hopefully not recursive..
35327         },this);
35328
35329         this.scroller.on("scroll", this.handleScroll, this);
35330         this.lockedBody.on("mousewheel", this.handleWheel, this);
35331         this.mainBody.on("mousewheel", this.handleWheel, this);
35332
35333         this.mainHd.on("mouseover", this.handleHdOver, this);
35334         this.mainHd.on("mouseout", this.handleHdOut, this);
35335         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
35336                 {delegate: "."+this.splitClass});
35337
35338         this.lockedHd.on("mouseover", this.handleHdOver, this);
35339         this.lockedHd.on("mouseout", this.handleHdOut, this);
35340         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
35341                 {delegate: "."+this.splitClass});
35342
35343         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
35344             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
35345         }
35346
35347         this.updateSplitters();
35348
35349         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
35350             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
35351             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
35352         }
35353
35354         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
35355             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
35356             this.hmenu.add(
35357                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
35358                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
35359             );
35360             if(this.grid.enableColLock !== false){
35361                 this.hmenu.add('-',
35362                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
35363                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
35364                 );
35365             }
35366             if(this.grid.enableColumnHide !== false){
35367
35368                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
35369                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
35370                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
35371
35372                 this.hmenu.add('-',
35373                     {id:"columns", text: this.columnsText, menu: this.colMenu}
35374                 );
35375             }
35376             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
35377
35378             this.grid.on("headercontextmenu", this.handleHdCtx, this);
35379         }
35380
35381         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
35382             this.dd = new Roo.grid.GridDragZone(this.grid, {
35383                 ddGroup : this.grid.ddGroup || 'GridDD'
35384             });
35385         }
35386
35387         /*
35388         for(var i = 0; i < colCount; i++){
35389             if(cm.isHidden(i)){
35390                 this.hideColumn(i);
35391             }
35392             if(cm.config[i].align){
35393                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
35394                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
35395             }
35396         }*/
35397         
35398         this.updateHeaderSortState();
35399
35400         this.beforeInitialResize();
35401         this.layout(true);
35402
35403         // two part rendering gives faster view to the user
35404         this.renderPhase2.defer(1, this);
35405     },
35406
35407     renderPhase2 : function(){
35408         // render the rows now
35409         this.refresh();
35410         if(this.grid.autoSizeColumns){
35411             this.autoSizeColumns();
35412         }
35413     },
35414
35415     beforeInitialResize : function(){
35416
35417     },
35418
35419     onColumnSplitterMoved : function(i, w){
35420         this.userResized = true;
35421         var cm = this.grid.colModel;
35422         cm.setColumnWidth(i, w, true);
35423         var cid = cm.getColumnId(i);
35424         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
35425         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
35426         this.updateSplitters();
35427         this.layout();
35428         this.grid.fireEvent("columnresize", i, w);
35429     },
35430
35431     syncRowHeights : function(startIndex, endIndex){
35432         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
35433             startIndex = startIndex || 0;
35434             var mrows = this.getBodyTable().rows;
35435             var lrows = this.getLockedTable().rows;
35436             var len = mrows.length-1;
35437             endIndex = Math.min(endIndex || len, len);
35438             for(var i = startIndex; i <= endIndex; i++){
35439                 var m = mrows[i], l = lrows[i];
35440                 var h = Math.max(m.offsetHeight, l.offsetHeight);
35441                 m.style.height = l.style.height = h + "px";
35442             }
35443         }
35444     },
35445
35446     layout : function(initialRender, is2ndPass){
35447         var g = this.grid;
35448         var auto = g.autoHeight;
35449         var scrollOffset = 16;
35450         var c = g.getGridEl(), cm = this.cm,
35451                 expandCol = g.autoExpandColumn,
35452                 gv = this;
35453         //c.beginMeasure();
35454
35455         if(!c.dom.offsetWidth){ // display:none?
35456             if(initialRender){
35457                 this.lockedWrap.show();
35458                 this.mainWrap.show();
35459             }
35460             return;
35461         }
35462
35463         var hasLock = this.cm.isLocked(0);
35464
35465         var tbh = this.headerPanel.getHeight();
35466         var bbh = this.footerPanel.getHeight();
35467
35468         if(auto){
35469             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
35470             var newHeight = ch + c.getBorderWidth("tb");
35471             if(g.maxHeight){
35472                 newHeight = Math.min(g.maxHeight, newHeight);
35473             }
35474             c.setHeight(newHeight);
35475         }
35476
35477         if(g.autoWidth){
35478             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
35479         }
35480
35481         var s = this.scroller;
35482
35483         var csize = c.getSize(true);
35484
35485         this.el.setSize(csize.width, csize.height);
35486
35487         this.headerPanel.setWidth(csize.width);
35488         this.footerPanel.setWidth(csize.width);
35489
35490         var hdHeight = this.mainHd.getHeight();
35491         var vw = csize.width;
35492         var vh = csize.height - (tbh + bbh);
35493
35494         s.setSize(vw, vh);
35495
35496         var bt = this.getBodyTable();
35497         var ltWidth = hasLock ?
35498                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
35499
35500         var scrollHeight = bt.offsetHeight;
35501         var scrollWidth = ltWidth + bt.offsetWidth;
35502         var vscroll = false, hscroll = false;
35503
35504         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
35505
35506         var lw = this.lockedWrap, mw = this.mainWrap;
35507         var lb = this.lockedBody, mb = this.mainBody;
35508
35509         setTimeout(function(){
35510             var t = s.dom.offsetTop;
35511             var w = s.dom.clientWidth,
35512                 h = s.dom.clientHeight;
35513
35514             lw.setTop(t);
35515             lw.setSize(ltWidth, h);
35516
35517             mw.setLeftTop(ltWidth, t);
35518             mw.setSize(w-ltWidth, h);
35519
35520             lb.setHeight(h-hdHeight);
35521             mb.setHeight(h-hdHeight);
35522
35523             if(is2ndPass !== true && !gv.userResized && expandCol){
35524                 // high speed resize without full column calculation
35525                 
35526                 var ci = cm.getIndexById(expandCol);
35527                 if (ci < 0) {
35528                     ci = cm.findColumnIndex(expandCol);
35529                 }
35530                 ci = Math.max(0, ci); // make sure it's got at least the first col.
35531                 var expandId = cm.getColumnId(ci);
35532                 var  tw = cm.getTotalWidth(false);
35533                 var currentWidth = cm.getColumnWidth(ci);
35534                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
35535                 if(currentWidth != cw){
35536                     cm.setColumnWidth(ci, cw, true);
35537                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
35538                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
35539                     gv.updateSplitters();
35540                     gv.layout(false, true);
35541                 }
35542             }
35543
35544             if(initialRender){
35545                 lw.show();
35546                 mw.show();
35547             }
35548             //c.endMeasure();
35549         }, 10);
35550     },
35551
35552     onWindowResize : function(){
35553         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
35554             return;
35555         }
35556         this.layout();
35557     },
35558
35559     appendFooter : function(parentEl){
35560         return null;
35561     },
35562
35563     sortAscText : "Sort Ascending",
35564     sortDescText : "Sort Descending",
35565     lockText : "Lock Column",
35566     unlockText : "Unlock Column",
35567     columnsText : "Columns"
35568 });
35569
35570
35571 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
35572     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
35573     this.proxy.el.addClass('x-grid3-col-dd');
35574 };
35575
35576 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
35577     handleMouseDown : function(e){
35578
35579     },
35580
35581     callHandleMouseDown : function(e){
35582         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
35583     }
35584 });
35585 /*
35586  * Based on:
35587  * Ext JS Library 1.1.1
35588  * Copyright(c) 2006-2007, Ext JS, LLC.
35589  *
35590  * Originally Released Under LGPL - original licence link has changed is not relivant.
35591  *
35592  * Fork - LGPL
35593  * <script type="text/javascript">
35594  */
35595  
35596 // private
35597 // This is a support class used internally by the Grid components
35598 Roo.grid.SplitDragZone = function(grid, hd, hd2){
35599     this.grid = grid;
35600     this.view = grid.getView();
35601     this.proxy = this.view.resizeProxy;
35602     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
35603         "gridSplitters" + this.grid.getGridEl().id, {
35604         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
35605     });
35606     this.setHandleElId(Roo.id(hd));
35607     this.setOuterHandleElId(Roo.id(hd2));
35608     this.scroll = false;
35609 };
35610 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
35611     fly: Roo.Element.fly,
35612
35613     b4StartDrag : function(x, y){
35614         this.view.headersDisabled = true;
35615         this.proxy.setHeight(this.view.mainWrap.getHeight());
35616         var w = this.cm.getColumnWidth(this.cellIndex);
35617         var minw = Math.max(w-this.grid.minColumnWidth, 0);
35618         this.resetConstraints();
35619         this.setXConstraint(minw, 1000);
35620         this.setYConstraint(0, 0);
35621         this.minX = x - minw;
35622         this.maxX = x + 1000;
35623         this.startPos = x;
35624         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
35625     },
35626
35627
35628     handleMouseDown : function(e){
35629         ev = Roo.EventObject.setEvent(e);
35630         var t = this.fly(ev.getTarget());
35631         if(t.hasClass("x-grid-split")){
35632             this.cellIndex = this.view.getCellIndex(t.dom);
35633             this.split = t.dom;
35634             this.cm = this.grid.colModel;
35635             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
35636                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
35637             }
35638         }
35639     },
35640
35641     endDrag : function(e){
35642         this.view.headersDisabled = false;
35643         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
35644         var diff = endX - this.startPos;
35645         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
35646     },
35647
35648     autoOffset : function(){
35649         this.setDelta(0,0);
35650     }
35651 });/*
35652  * Based on:
35653  * Ext JS Library 1.1.1
35654  * Copyright(c) 2006-2007, Ext JS, LLC.
35655  *
35656  * Originally Released Under LGPL - original licence link has changed is not relivant.
35657  *
35658  * Fork - LGPL
35659  * <script type="text/javascript">
35660  */
35661  
35662 // private
35663 // This is a support class used internally by the Grid components
35664 Roo.grid.GridDragZone = function(grid, config){
35665     this.view = grid.getView();
35666     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
35667     if(this.view.lockedBody){
35668         this.setHandleElId(Roo.id(this.view.mainBody.dom));
35669         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
35670     }
35671     this.scroll = false;
35672     this.grid = grid;
35673     this.ddel = document.createElement('div');
35674     this.ddel.className = 'x-grid-dd-wrap';
35675 };
35676
35677 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
35678     ddGroup : "GridDD",
35679
35680     getDragData : function(e){
35681         var t = Roo.lib.Event.getTarget(e);
35682         var rowIndex = this.view.findRowIndex(t);
35683         if(rowIndex !== false){
35684             var sm = this.grid.selModel;
35685             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
35686               //  sm.mouseDown(e, t);
35687             //}
35688             if (e.hasModifier()){
35689                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
35690             }
35691             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
35692         }
35693         return false;
35694     },
35695
35696     onInitDrag : function(e){
35697         var data = this.dragData;
35698         this.ddel.innerHTML = this.grid.getDragDropText();
35699         this.proxy.update(this.ddel);
35700         // fire start drag?
35701     },
35702
35703     afterRepair : function(){
35704         this.dragging = false;
35705     },
35706
35707     getRepairXY : function(e, data){
35708         return false;
35709     },
35710
35711     onEndDrag : function(data, e){
35712         // fire end drag?
35713     },
35714
35715     onValidDrop : function(dd, e, id){
35716         // fire drag drop?
35717         this.hideProxy();
35718     },
35719
35720     beforeInvalidDrop : function(e, id){
35721
35722     }
35723 });/*
35724  * Based on:
35725  * Ext JS Library 1.1.1
35726  * Copyright(c) 2006-2007, Ext JS, LLC.
35727  *
35728  * Originally Released Under LGPL - original licence link has changed is not relivant.
35729  *
35730  * Fork - LGPL
35731  * <script type="text/javascript">
35732  */
35733  
35734
35735 /**
35736  * @class Roo.grid.ColumnModel
35737  * @extends Roo.util.Observable
35738  * This is the default implementation of a ColumnModel used by the Grid. It defines
35739  * the columns in the grid.
35740  * <br>Usage:<br>
35741  <pre><code>
35742  var colModel = new Roo.grid.ColumnModel([
35743         {header: "Ticker", width: 60, sortable: true, locked: true},
35744         {header: "Company Name", width: 150, sortable: true},
35745         {header: "Market Cap.", width: 100, sortable: true},
35746         {header: "$ Sales", width: 100, sortable: true, renderer: money},
35747         {header: "Employees", width: 100, sortable: true, resizable: false}
35748  ]);
35749  </code></pre>
35750  * <p>
35751  
35752  * The config options listed for this class are options which may appear in each
35753  * individual column definition.
35754  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
35755  * @constructor
35756  * @param {Object} config An Array of column config objects. See this class's
35757  * config objects for details.
35758 */
35759 Roo.grid.ColumnModel = function(config){
35760         /**
35761      * The config passed into the constructor
35762      */
35763     this.config = config;
35764     this.lookup = {};
35765
35766     // if no id, create one
35767     // if the column does not have a dataIndex mapping,
35768     // map it to the order it is in the config
35769     for(var i = 0, len = config.length; i < len; i++){
35770         var c = config[i];
35771         if(typeof c.dataIndex == "undefined"){
35772             c.dataIndex = i;
35773         }
35774         if(typeof c.renderer == "string"){
35775             c.renderer = Roo.util.Format[c.renderer];
35776         }
35777         if(typeof c.id == "undefined"){
35778             c.id = Roo.id();
35779         }
35780         if(c.editor && c.editor.xtype){
35781             c.editor  = Roo.factory(c.editor, Roo.grid);
35782         }
35783         if(c.editor && c.editor.isFormField){
35784             c.editor = new Roo.grid.GridEditor(c.editor);
35785         }
35786         this.lookup[c.id] = c;
35787     }
35788
35789     /**
35790      * The width of columns which have no width specified (defaults to 100)
35791      * @type Number
35792      */
35793     this.defaultWidth = 100;
35794
35795     /**
35796      * Default sortable of columns which have no sortable specified (defaults to false)
35797      * @type Boolean
35798      */
35799     this.defaultSortable = false;
35800
35801     this.addEvents({
35802         /**
35803              * @event widthchange
35804              * Fires when the width of a column changes.
35805              * @param {ColumnModel} this
35806              * @param {Number} columnIndex The column index
35807              * @param {Number} newWidth The new width
35808              */
35809             "widthchange": true,
35810         /**
35811              * @event headerchange
35812              * Fires when the text of a header changes.
35813              * @param {ColumnModel} this
35814              * @param {Number} columnIndex The column index
35815              * @param {Number} newText The new header text
35816              */
35817             "headerchange": true,
35818         /**
35819              * @event hiddenchange
35820              * Fires when a column is hidden or "unhidden".
35821              * @param {ColumnModel} this
35822              * @param {Number} columnIndex The column index
35823              * @param {Boolean} hidden true if hidden, false otherwise
35824              */
35825             "hiddenchange": true,
35826             /**
35827          * @event columnmoved
35828          * Fires when a column is moved.
35829          * @param {ColumnModel} this
35830          * @param {Number} oldIndex
35831          * @param {Number} newIndex
35832          */
35833         "columnmoved" : true,
35834         /**
35835          * @event columlockchange
35836          * Fires when a column's locked state is changed
35837          * @param {ColumnModel} this
35838          * @param {Number} colIndex
35839          * @param {Boolean} locked true if locked
35840          */
35841         "columnlockchange" : true
35842     });
35843     Roo.grid.ColumnModel.superclass.constructor.call(this);
35844 };
35845 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
35846     /**
35847      * @cfg {String} header The header text to display in the Grid view.
35848      */
35849     /**
35850      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
35851      * {@link Roo.data.Record} definition from which to draw the column's value. If not
35852      * specified, the column's index is used as an index into the Record's data Array.
35853      */
35854     /**
35855      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
35856      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
35857      */
35858     /**
35859      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
35860      * Defaults to the value of the {@link #defaultSortable} property.
35861      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
35862      */
35863     /**
35864      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
35865      */
35866     /**
35867      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
35868      */
35869     /**
35870      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
35871      */
35872     /**
35873      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
35874      */
35875     /**
35876      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
35877      * given the cell's data value. See {@link #setRenderer}. If not specified, the
35878      * default renderer uses the raw data value.
35879      */
35880        /**
35881      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
35882      */
35883     /**
35884      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
35885      */
35886
35887     /**
35888      * Returns the id of the column at the specified index.
35889      * @param {Number} index The column index
35890      * @return {String} the id
35891      */
35892     getColumnId : function(index){
35893         return this.config[index].id;
35894     },
35895
35896     /**
35897      * Returns the column for a specified id.
35898      * @param {String} id The column id
35899      * @return {Object} the column
35900      */
35901     getColumnById : function(id){
35902         return this.lookup[id];
35903     },
35904
35905     
35906     /**
35907      * Returns the column for a specified dataIndex.
35908      * @param {String} dataIndex The column dataIndex
35909      * @return {Object|Boolean} the column or false if not found
35910      */
35911     getColumnByDataIndex: function(dataIndex){
35912         var index = this.findColumnIndex(dataIndex);
35913         return index > -1 ? this.config[index] : false;
35914     },
35915     
35916     /**
35917      * Returns the index for a specified column id.
35918      * @param {String} id The column id
35919      * @return {Number} the index, or -1 if not found
35920      */
35921     getIndexById : function(id){
35922         for(var i = 0, len = this.config.length; i < len; i++){
35923             if(this.config[i].id == id){
35924                 return i;
35925             }
35926         }
35927         return -1;
35928     },
35929     
35930     /**
35931      * Returns the index for a specified column dataIndex.
35932      * @param {String} dataIndex The column dataIndex
35933      * @return {Number} the index, or -1 if not found
35934      */
35935     
35936     findColumnIndex : function(dataIndex){
35937         for(var i = 0, len = this.config.length; i < len; i++){
35938             if(this.config[i].dataIndex == dataIndex){
35939                 return i;
35940             }
35941         }
35942         return -1;
35943     },
35944     
35945     
35946     moveColumn : function(oldIndex, newIndex){
35947         var c = this.config[oldIndex];
35948         this.config.splice(oldIndex, 1);
35949         this.config.splice(newIndex, 0, c);
35950         this.dataMap = null;
35951         this.fireEvent("columnmoved", this, oldIndex, newIndex);
35952     },
35953
35954     isLocked : function(colIndex){
35955         return this.config[colIndex].locked === true;
35956     },
35957
35958     setLocked : function(colIndex, value, suppressEvent){
35959         if(this.isLocked(colIndex) == value){
35960             return;
35961         }
35962         this.config[colIndex].locked = value;
35963         if(!suppressEvent){
35964             this.fireEvent("columnlockchange", this, colIndex, value);
35965         }
35966     },
35967
35968     getTotalLockedWidth : function(){
35969         var totalWidth = 0;
35970         for(var i = 0; i < this.config.length; i++){
35971             if(this.isLocked(i) && !this.isHidden(i)){
35972                 this.totalWidth += this.getColumnWidth(i);
35973             }
35974         }
35975         return totalWidth;
35976     },
35977
35978     getLockedCount : function(){
35979         for(var i = 0, len = this.config.length; i < len; i++){
35980             if(!this.isLocked(i)){
35981                 return i;
35982             }
35983         }
35984     },
35985
35986     /**
35987      * Returns the number of columns.
35988      * @return {Number}
35989      */
35990     getColumnCount : function(visibleOnly){
35991         if(visibleOnly === true){
35992             var c = 0;
35993             for(var i = 0, len = this.config.length; i < len; i++){
35994                 if(!this.isHidden(i)){
35995                     c++;
35996                 }
35997             }
35998             return c;
35999         }
36000         return this.config.length;
36001     },
36002
36003     /**
36004      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
36005      * @param {Function} fn
36006      * @param {Object} scope (optional)
36007      * @return {Array} result
36008      */
36009     getColumnsBy : function(fn, scope){
36010         var r = [];
36011         for(var i = 0, len = this.config.length; i < len; i++){
36012             var c = this.config[i];
36013             if(fn.call(scope||this, c, i) === true){
36014                 r[r.length] = c;
36015             }
36016         }
36017         return r;
36018     },
36019
36020     /**
36021      * Returns true if the specified column is sortable.
36022      * @param {Number} col The column index
36023      * @return {Boolean}
36024      */
36025     isSortable : function(col){
36026         if(typeof this.config[col].sortable == "undefined"){
36027             return this.defaultSortable;
36028         }
36029         return this.config[col].sortable;
36030     },
36031
36032     /**
36033      * Returns the rendering (formatting) function defined for the column.
36034      * @param {Number} col The column index.
36035      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
36036      */
36037     getRenderer : function(col){
36038         if(!this.config[col].renderer){
36039             return Roo.grid.ColumnModel.defaultRenderer;
36040         }
36041         return this.config[col].renderer;
36042     },
36043
36044     /**
36045      * Sets the rendering (formatting) function for a column.
36046      * @param {Number} col The column index
36047      * @param {Function} fn The function to use to process the cell's raw data
36048      * to return HTML markup for the grid view. The render function is called with
36049      * the following parameters:<ul>
36050      * <li>Data value.</li>
36051      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
36052      * <li>css A CSS style string to apply to the table cell.</li>
36053      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
36054      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
36055      * <li>Row index</li>
36056      * <li>Column index</li>
36057      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
36058      */
36059     setRenderer : function(col, fn){
36060         this.config[col].renderer = fn;
36061     },
36062
36063     /**
36064      * Returns the width for the specified column.
36065      * @param {Number} col The column index
36066      * @return {Number}
36067      */
36068     getColumnWidth : function(col){
36069         return this.config[col].width * 1 || this.defaultWidth;
36070     },
36071
36072     /**
36073      * Sets the width for a column.
36074      * @param {Number} col The column index
36075      * @param {Number} width The new width
36076      */
36077     setColumnWidth : function(col, width, suppressEvent){
36078         this.config[col].width = width;
36079         this.totalWidth = null;
36080         if(!suppressEvent){
36081              this.fireEvent("widthchange", this, col, width);
36082         }
36083     },
36084
36085     /**
36086      * Returns the total width of all columns.
36087      * @param {Boolean} includeHidden True to include hidden column widths
36088      * @return {Number}
36089      */
36090     getTotalWidth : function(includeHidden){
36091         if(!this.totalWidth){
36092             this.totalWidth = 0;
36093             for(var i = 0, len = this.config.length; i < len; i++){
36094                 if(includeHidden || !this.isHidden(i)){
36095                     this.totalWidth += this.getColumnWidth(i);
36096                 }
36097             }
36098         }
36099         return this.totalWidth;
36100     },
36101
36102     /**
36103      * Returns the header for the specified column.
36104      * @param {Number} col The column index
36105      * @return {String}
36106      */
36107     getColumnHeader : function(col){
36108         return this.config[col].header;
36109     },
36110
36111     /**
36112      * Sets the header for a column.
36113      * @param {Number} col The column index
36114      * @param {String} header The new header
36115      */
36116     setColumnHeader : function(col, header){
36117         this.config[col].header = header;
36118         this.fireEvent("headerchange", this, col, header);
36119     },
36120
36121     /**
36122      * Returns the tooltip for the specified column.
36123      * @param {Number} col The column index
36124      * @return {String}
36125      */
36126     getColumnTooltip : function(col){
36127             return this.config[col].tooltip;
36128     },
36129     /**
36130      * Sets the tooltip for a column.
36131      * @param {Number} col The column index
36132      * @param {String} tooltip The new tooltip
36133      */
36134     setColumnTooltip : function(col, tooltip){
36135             this.config[col].tooltip = tooltip;
36136     },
36137
36138     /**
36139      * Returns the dataIndex for the specified column.
36140      * @param {Number} col The column index
36141      * @return {Number}
36142      */
36143     getDataIndex : function(col){
36144         return this.config[col].dataIndex;
36145     },
36146
36147     /**
36148      * Sets the dataIndex for a column.
36149      * @param {Number} col The column index
36150      * @param {Number} dataIndex The new dataIndex
36151      */
36152     setDataIndex : function(col, dataIndex){
36153         this.config[col].dataIndex = dataIndex;
36154     },
36155
36156     
36157     
36158     /**
36159      * Returns true if the cell is editable.
36160      * @param {Number} colIndex The column index
36161      * @param {Number} rowIndex The row index
36162      * @return {Boolean}
36163      */
36164     isCellEditable : function(colIndex, rowIndex){
36165         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
36166     },
36167
36168     /**
36169      * Returns the editor defined for the cell/column.
36170      * return false or null to disable editing.
36171      * @param {Number} colIndex The column index
36172      * @param {Number} rowIndex The row index
36173      * @return {Object}
36174      */
36175     getCellEditor : function(colIndex, rowIndex){
36176         return this.config[colIndex].editor;
36177     },
36178
36179     /**
36180      * Sets if a column is editable.
36181      * @param {Number} col The column index
36182      * @param {Boolean} editable True if the column is editable
36183      */
36184     setEditable : function(col, editable){
36185         this.config[col].editable = editable;
36186     },
36187
36188
36189     /**
36190      * Returns true if the column is hidden.
36191      * @param {Number} colIndex The column index
36192      * @return {Boolean}
36193      */
36194     isHidden : function(colIndex){
36195         return this.config[colIndex].hidden;
36196     },
36197
36198
36199     /**
36200      * Returns true if the column width cannot be changed
36201      */
36202     isFixed : function(colIndex){
36203         return this.config[colIndex].fixed;
36204     },
36205
36206     /**
36207      * Returns true if the column can be resized
36208      * @return {Boolean}
36209      */
36210     isResizable : function(colIndex){
36211         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
36212     },
36213     /**
36214      * Sets if a column is hidden.
36215      * @param {Number} colIndex The column index
36216      * @param {Boolean} hidden True if the column is hidden
36217      */
36218     setHidden : function(colIndex, hidden){
36219         this.config[colIndex].hidden = hidden;
36220         this.totalWidth = null;
36221         this.fireEvent("hiddenchange", this, colIndex, hidden);
36222     },
36223
36224     /**
36225      * Sets the editor for a column.
36226      * @param {Number} col The column index
36227      * @param {Object} editor The editor object
36228      */
36229     setEditor : function(col, editor){
36230         this.config[col].editor = editor;
36231     }
36232 });
36233
36234 Roo.grid.ColumnModel.defaultRenderer = function(value){
36235         if(typeof value == "string" && value.length < 1){
36236             return "&#160;";
36237         }
36238         return value;
36239 };
36240
36241 // Alias for backwards compatibility
36242 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
36243 /*
36244  * Based on:
36245  * Ext JS Library 1.1.1
36246  * Copyright(c) 2006-2007, Ext JS, LLC.
36247  *
36248  * Originally Released Under LGPL - original licence link has changed is not relivant.
36249  *
36250  * Fork - LGPL
36251  * <script type="text/javascript">
36252  */
36253
36254 /**
36255  * @class Roo.grid.AbstractSelectionModel
36256  * @extends Roo.util.Observable
36257  * Abstract base class for grid SelectionModels.  It provides the interface that should be
36258  * implemented by descendant classes.  This class should not be directly instantiated.
36259  * @constructor
36260  */
36261 Roo.grid.AbstractSelectionModel = function(){
36262     this.locked = false;
36263     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
36264 };
36265
36266 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
36267     /** @ignore Called by the grid automatically. Do not call directly. */
36268     init : function(grid){
36269         this.grid = grid;
36270         this.initEvents();
36271     },
36272
36273     /**
36274      * Locks the selections.
36275      */
36276     lock : function(){
36277         this.locked = true;
36278     },
36279
36280     /**
36281      * Unlocks the selections.
36282      */
36283     unlock : function(){
36284         this.locked = false;
36285     },
36286
36287     /**
36288      * Returns true if the selections are locked.
36289      * @return {Boolean}
36290      */
36291     isLocked : function(){
36292         return this.locked;
36293     }
36294 });/*
36295  * Based on:
36296  * Ext JS Library 1.1.1
36297  * Copyright(c) 2006-2007, Ext JS, LLC.
36298  *
36299  * Originally Released Under LGPL - original licence link has changed is not relivant.
36300  *
36301  * Fork - LGPL
36302  * <script type="text/javascript">
36303  */
36304 /**
36305  * @extends Roo.grid.AbstractSelectionModel
36306  * @class Roo.grid.RowSelectionModel
36307  * The default SelectionModel used by {@link Roo.grid.Grid}.
36308  * It supports multiple selections and keyboard selection/navigation. 
36309  * @constructor
36310  * @param {Object} config
36311  */
36312 Roo.grid.RowSelectionModel = function(config){
36313     Roo.apply(this, config);
36314     this.selections = new Roo.util.MixedCollection(false, function(o){
36315         return o.id;
36316     });
36317
36318     this.last = false;
36319     this.lastActive = false;
36320
36321     this.addEvents({
36322         /**
36323              * @event selectionchange
36324              * Fires when the selection changes
36325              * @param {SelectionModel} this
36326              */
36327             "selectionchange" : true,
36328         /**
36329              * @event afterselectionchange
36330              * Fires after the selection changes (eg. by key press or clicking)
36331              * @param {SelectionModel} this
36332              */
36333             "afterselectionchange" : true,
36334         /**
36335              * @event beforerowselect
36336              * Fires when a row is selected being selected, return false to cancel.
36337              * @param {SelectionModel} this
36338              * @param {Number} rowIndex The selected index
36339              * @param {Boolean} keepExisting False if other selections will be cleared
36340              */
36341             "beforerowselect" : true,
36342         /**
36343              * @event rowselect
36344              * Fires when a row is selected.
36345              * @param {SelectionModel} this
36346              * @param {Number} rowIndex The selected index
36347              * @param {Roo.data.Record} r The record
36348              */
36349             "rowselect" : true,
36350         /**
36351              * @event rowdeselect
36352              * Fires when a row is deselected.
36353              * @param {SelectionModel} this
36354              * @param {Number} rowIndex The selected index
36355              */
36356         "rowdeselect" : true
36357     });
36358     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
36359     this.locked = false;
36360 };
36361
36362 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
36363     /**
36364      * @cfg {Boolean} singleSelect
36365      * True to allow selection of only one row at a time (defaults to false)
36366      */
36367     singleSelect : false,
36368
36369     // private
36370     initEvents : function(){
36371
36372         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
36373             this.grid.on("mousedown", this.handleMouseDown, this);
36374         }else{ // allow click to work like normal
36375             this.grid.on("rowclick", this.handleDragableRowClick, this);
36376         }
36377
36378         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
36379             "up" : function(e){
36380                 if(!e.shiftKey){
36381                     this.selectPrevious(e.shiftKey);
36382                 }else if(this.last !== false && this.lastActive !== false){
36383                     var last = this.last;
36384                     this.selectRange(this.last,  this.lastActive-1);
36385                     this.grid.getView().focusRow(this.lastActive);
36386                     if(last !== false){
36387                         this.last = last;
36388                     }
36389                 }else{
36390                     this.selectFirstRow();
36391                 }
36392                 this.fireEvent("afterselectionchange", this);
36393             },
36394             "down" : function(e){
36395                 if(!e.shiftKey){
36396                     this.selectNext(e.shiftKey);
36397                 }else if(this.last !== false && this.lastActive !== false){
36398                     var last = this.last;
36399                     this.selectRange(this.last,  this.lastActive+1);
36400                     this.grid.getView().focusRow(this.lastActive);
36401                     if(last !== false){
36402                         this.last = last;
36403                     }
36404                 }else{
36405                     this.selectFirstRow();
36406                 }
36407                 this.fireEvent("afterselectionchange", this);
36408             },
36409             scope: this
36410         });
36411
36412         var view = this.grid.view;
36413         view.on("refresh", this.onRefresh, this);
36414         view.on("rowupdated", this.onRowUpdated, this);
36415         view.on("rowremoved", this.onRemove, this);
36416     },
36417
36418     // private
36419     onRefresh : function(){
36420         var ds = this.grid.dataSource, i, v = this.grid.view;
36421         var s = this.selections;
36422         s.each(function(r){
36423             if((i = ds.indexOfId(r.id)) != -1){
36424                 v.onRowSelect(i);
36425             }else{
36426                 s.remove(r);
36427             }
36428         });
36429     },
36430
36431     // private
36432     onRemove : function(v, index, r){
36433         this.selections.remove(r);
36434     },
36435
36436     // private
36437     onRowUpdated : function(v, index, r){
36438         if(this.isSelected(r)){
36439             v.onRowSelect(index);
36440         }
36441     },
36442
36443     /**
36444      * Select records.
36445      * @param {Array} records The records to select
36446      * @param {Boolean} keepExisting (optional) True to keep existing selections
36447      */
36448     selectRecords : function(records, keepExisting){
36449         if(!keepExisting){
36450             this.clearSelections();
36451         }
36452         var ds = this.grid.dataSource;
36453         for(var i = 0, len = records.length; i < len; i++){
36454             this.selectRow(ds.indexOf(records[i]), true);
36455         }
36456     },
36457
36458     /**
36459      * Gets the number of selected rows.
36460      * @return {Number}
36461      */
36462     getCount : function(){
36463         return this.selections.length;
36464     },
36465
36466     /**
36467      * Selects the first row in the grid.
36468      */
36469     selectFirstRow : function(){
36470         this.selectRow(0);
36471     },
36472
36473     /**
36474      * Select the last row.
36475      * @param {Boolean} keepExisting (optional) True to keep existing selections
36476      */
36477     selectLastRow : function(keepExisting){
36478         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
36479     },
36480
36481     /**
36482      * Selects the row immediately following the last selected row.
36483      * @param {Boolean} keepExisting (optional) True to keep existing selections
36484      */
36485     selectNext : function(keepExisting){
36486         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
36487             this.selectRow(this.last+1, keepExisting);
36488             this.grid.getView().focusRow(this.last);
36489         }
36490     },
36491
36492     /**
36493      * Selects the row that precedes the last selected row.
36494      * @param {Boolean} keepExisting (optional) True to keep existing selections
36495      */
36496     selectPrevious : function(keepExisting){
36497         if(this.last){
36498             this.selectRow(this.last-1, keepExisting);
36499             this.grid.getView().focusRow(this.last);
36500         }
36501     },
36502
36503     /**
36504      * Returns the selected records
36505      * @return {Array} Array of selected records
36506      */
36507     getSelections : function(){
36508         return [].concat(this.selections.items);
36509     },
36510
36511     /**
36512      * Returns the first selected record.
36513      * @return {Record}
36514      */
36515     getSelected : function(){
36516         return this.selections.itemAt(0);
36517     },
36518
36519
36520     /**
36521      * Clears all selections.
36522      */
36523     clearSelections : function(fast){
36524         if(this.locked) return;
36525         if(fast !== true){
36526             var ds = this.grid.dataSource;
36527             var s = this.selections;
36528             s.each(function(r){
36529                 this.deselectRow(ds.indexOfId(r.id));
36530             }, this);
36531             s.clear();
36532         }else{
36533             this.selections.clear();
36534         }
36535         this.last = false;
36536     },
36537
36538
36539     /**
36540      * Selects all rows.
36541      */
36542     selectAll : function(){
36543         if(this.locked) return;
36544         this.selections.clear();
36545         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
36546             this.selectRow(i, true);
36547         }
36548     },
36549
36550     /**
36551      * Returns True if there is a selection.
36552      * @return {Boolean}
36553      */
36554     hasSelection : function(){
36555         return this.selections.length > 0;
36556     },
36557
36558     /**
36559      * Returns True if the specified row is selected.
36560      * @param {Number/Record} record The record or index of the record to check
36561      * @return {Boolean}
36562      */
36563     isSelected : function(index){
36564         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
36565         return (r && this.selections.key(r.id) ? true : false);
36566     },
36567
36568     /**
36569      * Returns True if the specified record id is selected.
36570      * @param {String} id The id of record to check
36571      * @return {Boolean}
36572      */
36573     isIdSelected : function(id){
36574         return (this.selections.key(id) ? true : false);
36575     },
36576
36577     // private
36578     handleMouseDown : function(e, t){
36579         var view = this.grid.getView(), rowIndex;
36580         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
36581             return;
36582         };
36583         if(e.shiftKey && this.last !== false){
36584             var last = this.last;
36585             this.selectRange(last, rowIndex, e.ctrlKey);
36586             this.last = last; // reset the last
36587             view.focusRow(rowIndex);
36588         }else{
36589             var isSelected = this.isSelected(rowIndex);
36590             if(e.button !== 0 && isSelected){
36591                 view.focusRow(rowIndex);
36592             }else if(e.ctrlKey && isSelected){
36593                 this.deselectRow(rowIndex);
36594             }else if(!isSelected){
36595                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
36596                 view.focusRow(rowIndex);
36597             }
36598         }
36599         this.fireEvent("afterselectionchange", this);
36600     },
36601     // private
36602     handleDragableRowClick :  function(grid, rowIndex, e) 
36603     {
36604         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
36605             this.selectRow(rowIndex, false);
36606             grid.view.focusRow(rowIndex);
36607              this.fireEvent("afterselectionchange", this);
36608         }
36609     },
36610     
36611     /**
36612      * Selects multiple rows.
36613      * @param {Array} rows Array of the indexes of the row to select
36614      * @param {Boolean} keepExisting (optional) True to keep existing selections
36615      */
36616     selectRows : function(rows, keepExisting){
36617         if(!keepExisting){
36618             this.clearSelections();
36619         }
36620         for(var i = 0, len = rows.length; i < len; i++){
36621             this.selectRow(rows[i], true);
36622         }
36623     },
36624
36625     /**
36626      * Selects a range of rows. All rows in between startRow and endRow are also selected.
36627      * @param {Number} startRow The index of the first row in the range
36628      * @param {Number} endRow The index of the last row in the range
36629      * @param {Boolean} keepExisting (optional) True to retain existing selections
36630      */
36631     selectRange : function(startRow, endRow, keepExisting){
36632         if(this.locked) return;
36633         if(!keepExisting){
36634             this.clearSelections();
36635         }
36636         if(startRow <= endRow){
36637             for(var i = startRow; i <= endRow; i++){
36638                 this.selectRow(i, true);
36639             }
36640         }else{
36641             for(var i = startRow; i >= endRow; i--){
36642                 this.selectRow(i, true);
36643             }
36644         }
36645     },
36646
36647     /**
36648      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
36649      * @param {Number} startRow The index of the first row in the range
36650      * @param {Number} endRow The index of the last row in the range
36651      */
36652     deselectRange : function(startRow, endRow, preventViewNotify){
36653         if(this.locked) return;
36654         for(var i = startRow; i <= endRow; i++){
36655             this.deselectRow(i, preventViewNotify);
36656         }
36657     },
36658
36659     /**
36660      * Selects a row.
36661      * @param {Number} row The index of the row to select
36662      * @param {Boolean} keepExisting (optional) True to keep existing selections
36663      */
36664     selectRow : function(index, keepExisting, preventViewNotify){
36665         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
36666         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
36667             if(!keepExisting || this.singleSelect){
36668                 this.clearSelections();
36669             }
36670             var r = this.grid.dataSource.getAt(index);
36671             this.selections.add(r);
36672             this.last = this.lastActive = index;
36673             if(!preventViewNotify){
36674                 this.grid.getView().onRowSelect(index);
36675             }
36676             this.fireEvent("rowselect", this, index, r);
36677             this.fireEvent("selectionchange", this);
36678         }
36679     },
36680
36681     /**
36682      * Deselects a row.
36683      * @param {Number} row The index of the row to deselect
36684      */
36685     deselectRow : function(index, preventViewNotify){
36686         if(this.locked) return;
36687         if(this.last == index){
36688             this.last = false;
36689         }
36690         if(this.lastActive == index){
36691             this.lastActive = false;
36692         }
36693         var r = this.grid.dataSource.getAt(index);
36694         this.selections.remove(r);
36695         if(!preventViewNotify){
36696             this.grid.getView().onRowDeselect(index);
36697         }
36698         this.fireEvent("rowdeselect", this, index);
36699         this.fireEvent("selectionchange", this);
36700     },
36701
36702     // private
36703     restoreLast : function(){
36704         if(this._last){
36705             this.last = this._last;
36706         }
36707     },
36708
36709     // private
36710     acceptsNav : function(row, col, cm){
36711         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36712     },
36713
36714     // private
36715     onEditorKey : function(field, e){
36716         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
36717         if(k == e.TAB){
36718             e.stopEvent();
36719             ed.completeEdit();
36720             if(e.shiftKey){
36721                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36722             }else{
36723                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36724             }
36725         }else if(k == e.ENTER && !e.ctrlKey){
36726             e.stopEvent();
36727             ed.completeEdit();
36728             if(e.shiftKey){
36729                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
36730             }else{
36731                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
36732             }
36733         }else if(k == e.ESC){
36734             ed.cancelEdit();
36735         }
36736         if(newCell){
36737             g.startEditing(newCell[0], newCell[1]);
36738         }
36739     }
36740 });/*
36741  * Based on:
36742  * Ext JS Library 1.1.1
36743  * Copyright(c) 2006-2007, Ext JS, LLC.
36744  *
36745  * Originally Released Under LGPL - original licence link has changed is not relivant.
36746  *
36747  * Fork - LGPL
36748  * <script type="text/javascript">
36749  */
36750 /**
36751  * @class Roo.grid.CellSelectionModel
36752  * @extends Roo.grid.AbstractSelectionModel
36753  * This class provides the basic implementation for cell selection in a grid.
36754  * @constructor
36755  * @param {Object} config The object containing the configuration of this model.
36756  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
36757  */
36758 Roo.grid.CellSelectionModel = function(config){
36759     Roo.apply(this, config);
36760
36761     this.selection = null;
36762
36763     this.addEvents({
36764         /**
36765              * @event beforerowselect
36766              * Fires before a cell is selected.
36767              * @param {SelectionModel} this
36768              * @param {Number} rowIndex The selected row index
36769              * @param {Number} colIndex The selected cell index
36770              */
36771             "beforecellselect" : true,
36772         /**
36773              * @event cellselect
36774              * Fires when a cell is selected.
36775              * @param {SelectionModel} this
36776              * @param {Number} rowIndex The selected row index
36777              * @param {Number} colIndex The selected cell index
36778              */
36779             "cellselect" : true,
36780         /**
36781              * @event selectionchange
36782              * Fires when the active selection changes.
36783              * @param {SelectionModel} this
36784              * @param {Object} selection null for no selection or an object (o) with two properties
36785                 <ul>
36786                 <li>o.record: the record object for the row the selection is in</li>
36787                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
36788                 </ul>
36789              */
36790             "selectionchange" : true,
36791         /**
36792              * @event tabend
36793              * Fires when the tab (or enter) was pressed on the last editable cell
36794              * You can use this to trigger add new row.
36795              * @param {SelectionModel} this
36796              */
36797             "tabend" : true
36798     });
36799     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
36800 };
36801
36802 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
36803     
36804     enter_is_tab: false,
36805
36806     /** @ignore */
36807     initEvents : function(){
36808         this.grid.on("mousedown", this.handleMouseDown, this);
36809         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
36810         var view = this.grid.view;
36811         view.on("refresh", this.onViewChange, this);
36812         view.on("rowupdated", this.onRowUpdated, this);
36813         view.on("beforerowremoved", this.clearSelections, this);
36814         view.on("beforerowsinserted", this.clearSelections, this);
36815         if(this.grid.isEditor){
36816             this.grid.on("beforeedit", this.beforeEdit,  this);
36817         }
36818     },
36819
36820         //private
36821     beforeEdit : function(e){
36822         this.select(e.row, e.column, false, true, e.record);
36823     },
36824
36825         //private
36826     onRowUpdated : function(v, index, r){
36827         if(this.selection && this.selection.record == r){
36828             v.onCellSelect(index, this.selection.cell[1]);
36829         }
36830     },
36831
36832         //private
36833     onViewChange : function(){
36834         this.clearSelections(true);
36835     },
36836
36837         /**
36838          * Returns the currently selected cell,.
36839          * @return {Array} The selected cell (row, column) or null if none selected.
36840          */
36841     getSelectedCell : function(){
36842         return this.selection ? this.selection.cell : null;
36843     },
36844
36845     /**
36846      * Clears all selections.
36847      * @param {Boolean} true to prevent the gridview from being notified about the change.
36848      */
36849     clearSelections : function(preventNotify){
36850         var s = this.selection;
36851         if(s){
36852             if(preventNotify !== true){
36853                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
36854             }
36855             this.selection = null;
36856             this.fireEvent("selectionchange", this, null);
36857         }
36858     },
36859
36860     /**
36861      * Returns true if there is a selection.
36862      * @return {Boolean}
36863      */
36864     hasSelection : function(){
36865         return this.selection ? true : false;
36866     },
36867
36868     /** @ignore */
36869     handleMouseDown : function(e, t){
36870         var v = this.grid.getView();
36871         if(this.isLocked()){
36872             return;
36873         };
36874         var row = v.findRowIndex(t);
36875         var cell = v.findCellIndex(t);
36876         if(row !== false && cell !== false){
36877             this.select(row, cell);
36878         }
36879     },
36880
36881     /**
36882      * Selects a cell.
36883      * @param {Number} rowIndex
36884      * @param {Number} collIndex
36885      */
36886     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
36887         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
36888             this.clearSelections();
36889             r = r || this.grid.dataSource.getAt(rowIndex);
36890             this.selection = {
36891                 record : r,
36892                 cell : [rowIndex, colIndex]
36893             };
36894             if(!preventViewNotify){
36895                 var v = this.grid.getView();
36896                 v.onCellSelect(rowIndex, colIndex);
36897                 if(preventFocus !== true){
36898                     v.focusCell(rowIndex, colIndex);
36899                 }
36900             }
36901             this.fireEvent("cellselect", this, rowIndex, colIndex);
36902             this.fireEvent("selectionchange", this, this.selection);
36903         }
36904     },
36905
36906         //private
36907     isSelectable : function(rowIndex, colIndex, cm){
36908         return !cm.isHidden(colIndex);
36909     },
36910
36911     /** @ignore */
36912     handleKeyDown : function(e){
36913         //Roo.log('Cell Sel Model handleKeyDown');
36914         if(!e.isNavKeyPress()){
36915             return;
36916         }
36917         var g = this.grid, s = this.selection;
36918         if(!s){
36919             e.stopEvent();
36920             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
36921             if(cell){
36922                 this.select(cell[0], cell[1]);
36923             }
36924             return;
36925         }
36926         var sm = this;
36927         var walk = function(row, col, step){
36928             return g.walkCells(row, col, step, sm.isSelectable,  sm);
36929         };
36930         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
36931         var newCell;
36932
36933       
36934
36935         switch(k){
36936             case e.TAB:
36937                 // handled by onEditorKey
36938                 if (g.isEditor && g.editing) {
36939                     return;
36940                 }
36941                 if(e.shiftKey) {
36942                     newCell = walk(r, c-1, -1);
36943                 } else {
36944                     newCell = walk(r, c+1, 1);
36945                 }
36946                 break;
36947             
36948             case e.DOWN:
36949                newCell = walk(r+1, c, 1);
36950                 break;
36951             
36952             case e.UP:
36953                 newCell = walk(r-1, c, -1);
36954                 break;
36955             
36956             case e.RIGHT:
36957                 newCell = walk(r, c+1, 1);
36958                 break;
36959             
36960             case e.LEFT:
36961                 newCell = walk(r, c-1, -1);
36962                 break;
36963             
36964             case e.ENTER:
36965                 
36966                 if(g.isEditor && !g.editing){
36967                    g.startEditing(r, c);
36968                    e.stopEvent();
36969                    return;
36970                 }
36971                 
36972                 
36973              break;
36974         };
36975         if(newCell){
36976             this.select(newCell[0], newCell[1]);
36977             e.stopEvent();
36978             
36979         }
36980     },
36981
36982     acceptsNav : function(row, col, cm){
36983         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36984     },
36985     /**
36986      * Selects a cell.
36987      * @param {Number} field (not used) - as it's normally used as a listener
36988      * @param {Number} e - event - fake it by using
36989      *
36990      * var e = Roo.EventObjectImpl.prototype;
36991      * e.keyCode = e.TAB
36992      *
36993      * 
36994      */
36995     onEditorKey : function(field, e){
36996         
36997         var k = e.getKey(),
36998             newCell,
36999             g = this.grid,
37000             ed = g.activeEditor,
37001             forward = false;
37002         ///Roo.log('onEditorKey' + k);
37003         
37004         
37005         if (this.enter_is_tab && k == e.ENTER) {
37006             k = e.TAB;
37007         }
37008         
37009         if(k == e.TAB){
37010             if(e.shiftKey){
37011                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
37012             }else{
37013                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
37014                 forward = true;
37015             }
37016             
37017             e.stopEvent();
37018             
37019         }else if(k == e.ENTER &&  !e.ctrlKey){
37020             ed.completeEdit();
37021             e.stopEvent();
37022             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
37023         }else if(k == e.ESC){
37024             ed.cancelEdit();
37025         }
37026         
37027         
37028         if(newCell){
37029             //Roo.log('next cell after edit');
37030             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
37031         } else if (forward) {
37032             // tabbed past last
37033             this.fireEvent.defer(100, this, ['tabend',this]);
37034         }
37035     }
37036 });/*
37037  * Based on:
37038  * Ext JS Library 1.1.1
37039  * Copyright(c) 2006-2007, Ext JS, LLC.
37040  *
37041  * Originally Released Under LGPL - original licence link has changed is not relivant.
37042  *
37043  * Fork - LGPL
37044  * <script type="text/javascript">
37045  */
37046  
37047 /**
37048  * @class Roo.grid.EditorGrid
37049  * @extends Roo.grid.Grid
37050  * Class for creating and editable grid.
37051  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
37052  * The container MUST have some type of size defined for the grid to fill. The container will be 
37053  * automatically set to position relative if it isn't already.
37054  * @param {Object} dataSource The data model to bind to
37055  * @param {Object} colModel The column model with info about this grid's columns
37056  */
37057 Roo.grid.EditorGrid = function(container, config){
37058     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
37059     this.getGridEl().addClass("xedit-grid");
37060
37061     if(!this.selModel){
37062         this.selModel = new Roo.grid.CellSelectionModel();
37063     }
37064
37065     this.activeEditor = null;
37066
37067         this.addEvents({
37068             /**
37069              * @event beforeedit
37070              * Fires before cell editing is triggered. The edit event object has the following properties <br />
37071              * <ul style="padding:5px;padding-left:16px;">
37072              * <li>grid - This grid</li>
37073              * <li>record - The record being edited</li>
37074              * <li>field - The field name being edited</li>
37075              * <li>value - The value for the field being edited.</li>
37076              * <li>row - The grid row index</li>
37077              * <li>column - The grid column index</li>
37078              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
37079              * </ul>
37080              * @param {Object} e An edit event (see above for description)
37081              */
37082             "beforeedit" : true,
37083             /**
37084              * @event afteredit
37085              * Fires after a cell is edited. <br />
37086              * <ul style="padding:5px;padding-left:16px;">
37087              * <li>grid - This grid</li>
37088              * <li>record - The record being edited</li>
37089              * <li>field - The field name being edited</li>
37090              * <li>value - The value being set</li>
37091              * <li>originalValue - The original value for the field, before the edit.</li>
37092              * <li>row - The grid row index</li>
37093              * <li>column - The grid column index</li>
37094              * </ul>
37095              * @param {Object} e An edit event (see above for description)
37096              */
37097             "afteredit" : true,
37098             /**
37099              * @event validateedit
37100              * Fires after a cell is edited, but before the value is set in the record. 
37101          * You can use this to modify the value being set in the field, Return false
37102              * to cancel the change. The edit event object has the following properties <br />
37103              * <ul style="padding:5px;padding-left:16px;">
37104          * <li>editor - This editor</li>
37105              * <li>grid - This grid</li>
37106              * <li>record - The record being edited</li>
37107              * <li>field - The field name being edited</li>
37108              * <li>value - The value being set</li>
37109              * <li>originalValue - The original value for the field, before the edit.</li>
37110              * <li>row - The grid row index</li>
37111              * <li>column - The grid column index</li>
37112              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
37113              * </ul>
37114              * @param {Object} e An edit event (see above for description)
37115              */
37116             "validateedit" : true
37117         });
37118     this.on("bodyscroll", this.stopEditing,  this);
37119     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
37120 };
37121
37122 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
37123     /**
37124      * @cfg {Number} clicksToEdit
37125      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
37126      */
37127     clicksToEdit: 2,
37128
37129     // private
37130     isEditor : true,
37131     // private
37132     trackMouseOver: false, // causes very odd FF errors
37133
37134     onCellDblClick : function(g, row, col){
37135         this.startEditing(row, col);
37136     },
37137
37138     onEditComplete : function(ed, value, startValue){
37139         this.editing = false;
37140         this.activeEditor = null;
37141         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
37142         var r = ed.record;
37143         var field = this.colModel.getDataIndex(ed.col);
37144         var e = {
37145             grid: this,
37146             record: r,
37147             field: field,
37148             originalValue: startValue,
37149             value: value,
37150             row: ed.row,
37151             column: ed.col,
37152             cancel:false,
37153             editor: ed
37154         };
37155         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
37156         cell.show();
37157           
37158         if(String(value) !== String(startValue)){
37159             
37160             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
37161                 r.set(field, e.value);
37162                 // if we are dealing with a combo box..
37163                 // then we also set the 'name' colum to be the displayField
37164                 if (ed.field.displayField && ed.field.name) {
37165                     r.set(ed.field.name, ed.field.el.dom.value);
37166                 }
37167                 
37168                 delete e.cancel; //?? why!!!
37169                 this.fireEvent("afteredit", e);
37170             }
37171         } else {
37172             this.fireEvent("afteredit", e); // always fire it!
37173         }
37174         this.view.focusCell(ed.row, ed.col);
37175     },
37176
37177     /**
37178      * Starts editing the specified for the specified row/column
37179      * @param {Number} rowIndex
37180      * @param {Number} colIndex
37181      */
37182     startEditing : function(row, col){
37183         this.stopEditing();
37184         if(this.colModel.isCellEditable(col, row)){
37185             this.view.ensureVisible(row, col, true);
37186           
37187             var r = this.dataSource.getAt(row);
37188             var field = this.colModel.getDataIndex(col);
37189             var cell = Roo.get(this.view.getCell(row,col));
37190             var e = {
37191                 grid: this,
37192                 record: r,
37193                 field: field,
37194                 value: r.data[field],
37195                 row: row,
37196                 column: col,
37197                 cancel:false 
37198             };
37199             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
37200                 this.editing = true;
37201                 var ed = this.colModel.getCellEditor(col, row);
37202                 
37203                 if (!ed) {
37204                     return;
37205                 }
37206                 if(!ed.rendered){
37207                     ed.render(ed.parentEl || document.body);
37208                 }
37209                 ed.field.reset();
37210                
37211                 cell.hide();
37212                 
37213                 (function(){ // complex but required for focus issues in safari, ie and opera
37214                     ed.row = row;
37215                     ed.col = col;
37216                     ed.record = r;
37217                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
37218                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
37219                     this.activeEditor = ed;
37220                     var v = r.data[field];
37221                     ed.startEdit(this.view.getCell(row, col), v);
37222                     // combo's with 'displayField and name set
37223                     if (ed.field.displayField && ed.field.name) {
37224                         ed.field.el.dom.value = r.data[ed.field.name];
37225                     }
37226                     
37227                     
37228                 }).defer(50, this);
37229             }
37230         }
37231     },
37232         
37233     /**
37234      * Stops any active editing
37235      */
37236     stopEditing : function(){
37237         if(this.activeEditor){
37238             this.activeEditor.completeEdit();
37239         }
37240         this.activeEditor = null;
37241     }
37242 });/*
37243  * Based on:
37244  * Ext JS Library 1.1.1
37245  * Copyright(c) 2006-2007, Ext JS, LLC.
37246  *
37247  * Originally Released Under LGPL - original licence link has changed is not relivant.
37248  *
37249  * Fork - LGPL
37250  * <script type="text/javascript">
37251  */
37252
37253 // private - not really -- you end up using it !
37254 // This is a support class used internally by the Grid components
37255
37256 /**
37257  * @class Roo.grid.GridEditor
37258  * @extends Roo.Editor
37259  * Class for creating and editable grid elements.
37260  * @param {Object} config any settings (must include field)
37261  */
37262 Roo.grid.GridEditor = function(field, config){
37263     if (!config && field.field) {
37264         config = field;
37265         field = Roo.factory(config.field, Roo.form);
37266     }
37267     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
37268     field.monitorTab = false;
37269 };
37270
37271 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
37272     
37273     /**
37274      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
37275      */
37276     
37277     alignment: "tl-tl",
37278     autoSize: "width",
37279     hideEl : false,
37280     cls: "x-small-editor x-grid-editor",
37281     shim:false,
37282     shadow:"frame"
37283 });/*
37284  * Based on:
37285  * Ext JS Library 1.1.1
37286  * Copyright(c) 2006-2007, Ext JS, LLC.
37287  *
37288  * Originally Released Under LGPL - original licence link has changed is not relivant.
37289  *
37290  * Fork - LGPL
37291  * <script type="text/javascript">
37292  */
37293   
37294
37295   
37296 Roo.grid.PropertyRecord = Roo.data.Record.create([
37297     {name:'name',type:'string'},  'value'
37298 ]);
37299
37300
37301 Roo.grid.PropertyStore = function(grid, source){
37302     this.grid = grid;
37303     this.store = new Roo.data.Store({
37304         recordType : Roo.grid.PropertyRecord
37305     });
37306     this.store.on('update', this.onUpdate,  this);
37307     if(source){
37308         this.setSource(source);
37309     }
37310     Roo.grid.PropertyStore.superclass.constructor.call(this);
37311 };
37312
37313
37314
37315 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
37316     setSource : function(o){
37317         this.source = o;
37318         this.store.removeAll();
37319         var data = [];
37320         for(var k in o){
37321             if(this.isEditableValue(o[k])){
37322                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
37323             }
37324         }
37325         this.store.loadRecords({records: data}, {}, true);
37326     },
37327
37328     onUpdate : function(ds, record, type){
37329         if(type == Roo.data.Record.EDIT){
37330             var v = record.data['value'];
37331             var oldValue = record.modified['value'];
37332             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
37333                 this.source[record.id] = v;
37334                 record.commit();
37335                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
37336             }else{
37337                 record.reject();
37338             }
37339         }
37340     },
37341
37342     getProperty : function(row){
37343        return this.store.getAt(row);
37344     },
37345
37346     isEditableValue: function(val){
37347         if(val && val instanceof Date){
37348             return true;
37349         }else if(typeof val == 'object' || typeof val == 'function'){
37350             return false;
37351         }
37352         return true;
37353     },
37354
37355     setValue : function(prop, value){
37356         this.source[prop] = value;
37357         this.store.getById(prop).set('value', value);
37358     },
37359
37360     getSource : function(){
37361         return this.source;
37362     }
37363 });
37364
37365 Roo.grid.PropertyColumnModel = function(grid, store){
37366     this.grid = grid;
37367     var g = Roo.grid;
37368     g.PropertyColumnModel.superclass.constructor.call(this, [
37369         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
37370         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
37371     ]);
37372     this.store = store;
37373     this.bselect = Roo.DomHelper.append(document.body, {
37374         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
37375             {tag: 'option', value: 'true', html: 'true'},
37376             {tag: 'option', value: 'false', html: 'false'}
37377         ]
37378     });
37379     Roo.id(this.bselect);
37380     var f = Roo.form;
37381     this.editors = {
37382         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
37383         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
37384         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
37385         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
37386         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
37387     };
37388     this.renderCellDelegate = this.renderCell.createDelegate(this);
37389     this.renderPropDelegate = this.renderProp.createDelegate(this);
37390 };
37391
37392 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
37393     
37394     
37395     nameText : 'Name',
37396     valueText : 'Value',
37397     
37398     dateFormat : 'm/j/Y',
37399     
37400     
37401     renderDate : function(dateVal){
37402         return dateVal.dateFormat(this.dateFormat);
37403     },
37404
37405     renderBool : function(bVal){
37406         return bVal ? 'true' : 'false';
37407     },
37408
37409     isCellEditable : function(colIndex, rowIndex){
37410         return colIndex == 1;
37411     },
37412
37413     getRenderer : function(col){
37414         return col == 1 ?
37415             this.renderCellDelegate : this.renderPropDelegate;
37416     },
37417
37418     renderProp : function(v){
37419         return this.getPropertyName(v);
37420     },
37421
37422     renderCell : function(val){
37423         var rv = val;
37424         if(val instanceof Date){
37425             rv = this.renderDate(val);
37426         }else if(typeof val == 'boolean'){
37427             rv = this.renderBool(val);
37428         }
37429         return Roo.util.Format.htmlEncode(rv);
37430     },
37431
37432     getPropertyName : function(name){
37433         var pn = this.grid.propertyNames;
37434         return pn && pn[name] ? pn[name] : name;
37435     },
37436
37437     getCellEditor : function(colIndex, rowIndex){
37438         var p = this.store.getProperty(rowIndex);
37439         var n = p.data['name'], val = p.data['value'];
37440         
37441         if(typeof(this.grid.customEditors[n]) == 'string'){
37442             return this.editors[this.grid.customEditors[n]];
37443         }
37444         if(typeof(this.grid.customEditors[n]) != 'undefined'){
37445             return this.grid.customEditors[n];
37446         }
37447         if(val instanceof Date){
37448             return this.editors['date'];
37449         }else if(typeof val == 'number'){
37450             return this.editors['number'];
37451         }else if(typeof val == 'boolean'){
37452             return this.editors['boolean'];
37453         }else{
37454             return this.editors['string'];
37455         }
37456     }
37457 });
37458
37459 /**
37460  * @class Roo.grid.PropertyGrid
37461  * @extends Roo.grid.EditorGrid
37462  * This class represents the  interface of a component based property grid control.
37463  * <br><br>Usage:<pre><code>
37464  var grid = new Roo.grid.PropertyGrid("my-container-id", {
37465       
37466  });
37467  // set any options
37468  grid.render();
37469  * </code></pre>
37470   
37471  * @constructor
37472  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37473  * The container MUST have some type of size defined for the grid to fill. The container will be
37474  * automatically set to position relative if it isn't already.
37475  * @param {Object} config A config object that sets properties on this grid.
37476  */
37477 Roo.grid.PropertyGrid = function(container, config){
37478     config = config || {};
37479     var store = new Roo.grid.PropertyStore(this);
37480     this.store = store;
37481     var cm = new Roo.grid.PropertyColumnModel(this, store);
37482     store.store.sort('name', 'ASC');
37483     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
37484         ds: store.store,
37485         cm: cm,
37486         enableColLock:false,
37487         enableColumnMove:false,
37488         stripeRows:false,
37489         trackMouseOver: false,
37490         clicksToEdit:1
37491     }, config));
37492     this.getGridEl().addClass('x-props-grid');
37493     this.lastEditRow = null;
37494     this.on('columnresize', this.onColumnResize, this);
37495     this.addEvents({
37496          /**
37497              * @event beforepropertychange
37498              * Fires before a property changes (return false to stop?)
37499              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37500              * @param {String} id Record Id
37501              * @param {String} newval New Value
37502          * @param {String} oldval Old Value
37503              */
37504         "beforepropertychange": true,
37505         /**
37506              * @event propertychange
37507              * Fires after a property changes
37508              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37509              * @param {String} id Record Id
37510              * @param {String} newval New Value
37511          * @param {String} oldval Old Value
37512              */
37513         "propertychange": true
37514     });
37515     this.customEditors = this.customEditors || {};
37516 };
37517 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
37518     
37519      /**
37520      * @cfg {Object} customEditors map of colnames=> custom editors.
37521      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
37522      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
37523      * false disables editing of the field.
37524          */
37525     
37526       /**
37527      * @cfg {Object} propertyNames map of property Names to their displayed value
37528          */
37529     
37530     render : function(){
37531         Roo.grid.PropertyGrid.superclass.render.call(this);
37532         this.autoSize.defer(100, this);
37533     },
37534
37535     autoSize : function(){
37536         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
37537         if(this.view){
37538             this.view.fitColumns();
37539         }
37540     },
37541
37542     onColumnResize : function(){
37543         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
37544         this.autoSize();
37545     },
37546     /**
37547      * Sets the data for the Grid
37548      * accepts a Key => Value object of all the elements avaiable.
37549      * @param {Object} data  to appear in grid.
37550      */
37551     setSource : function(source){
37552         this.store.setSource(source);
37553         //this.autoSize();
37554     },
37555     /**
37556      * Gets all the data from the grid.
37557      * @return {Object} data  data stored in grid
37558      */
37559     getSource : function(){
37560         return this.store.getSource();
37561     }
37562 });/*
37563  * Based on:
37564  * Ext JS Library 1.1.1
37565  * Copyright(c) 2006-2007, Ext JS, LLC.
37566  *
37567  * Originally Released Under LGPL - original licence link has changed is not relivant.
37568  *
37569  * Fork - LGPL
37570  * <script type="text/javascript">
37571  */
37572  
37573 /**
37574  * @class Roo.LoadMask
37575  * A simple utility class for generically masking elements while loading data.  If the element being masked has
37576  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
37577  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
37578  * element's UpdateManager load indicator and will be destroyed after the initial load.
37579  * @constructor
37580  * Create a new LoadMask
37581  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
37582  * @param {Object} config The config object
37583  */
37584 Roo.LoadMask = function(el, config){
37585     this.el = Roo.get(el);
37586     Roo.apply(this, config);
37587     if(this.store){
37588         this.store.on('beforeload', this.onBeforeLoad, this);
37589         this.store.on('load', this.onLoad, this);
37590         this.store.on('loadexception', this.onLoadException, this);
37591         this.removeMask = false;
37592     }else{
37593         var um = this.el.getUpdateManager();
37594         um.showLoadIndicator = false; // disable the default indicator
37595         um.on('beforeupdate', this.onBeforeLoad, this);
37596         um.on('update', this.onLoad, this);
37597         um.on('failure', this.onLoad, this);
37598         this.removeMask = true;
37599     }
37600 };
37601
37602 Roo.LoadMask.prototype = {
37603     /**
37604      * @cfg {Boolean} removeMask
37605      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
37606      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
37607      */
37608     /**
37609      * @cfg {String} msg
37610      * The text to display in a centered loading message box (defaults to 'Loading...')
37611      */
37612     msg : 'Loading...',
37613     /**
37614      * @cfg {String} msgCls
37615      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
37616      */
37617     msgCls : 'x-mask-loading',
37618
37619     /**
37620      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
37621      * @type Boolean
37622      */
37623     disabled: false,
37624
37625     /**
37626      * Disables the mask to prevent it from being displayed
37627      */
37628     disable : function(){
37629        this.disabled = true;
37630     },
37631
37632     /**
37633      * Enables the mask so that it can be displayed
37634      */
37635     enable : function(){
37636         this.disabled = false;
37637     },
37638     
37639     onLoadException : function()
37640     {
37641         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
37642             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
37643         }
37644         this.el.unmask(this.removeMask);
37645     },
37646     // private
37647     onLoad : function()
37648     {
37649         this.el.unmask(this.removeMask);
37650     },
37651
37652     // private
37653     onBeforeLoad : function(){
37654         if(!this.disabled){
37655             this.el.mask(this.msg, this.msgCls);
37656         }
37657     },
37658
37659     // private
37660     destroy : function(){
37661         if(this.store){
37662             this.store.un('beforeload', this.onBeforeLoad, this);
37663             this.store.un('load', this.onLoad, this);
37664             this.store.un('loadexception', this.onLoadException, this);
37665         }else{
37666             var um = this.el.getUpdateManager();
37667             um.un('beforeupdate', this.onBeforeLoad, this);
37668             um.un('update', this.onLoad, this);
37669             um.un('failure', this.onLoad, this);
37670         }
37671     }
37672 };/*
37673  * Based on:
37674  * Ext JS Library 1.1.1
37675  * Copyright(c) 2006-2007, Ext JS, LLC.
37676  *
37677  * Originally Released Under LGPL - original licence link has changed is not relivant.
37678  *
37679  * Fork - LGPL
37680  * <script type="text/javascript">
37681  */
37682 Roo.XTemplate = function(){
37683     Roo.XTemplate.superclass.constructor.apply(this, arguments);
37684     var s = this.html;
37685
37686     s = ['<tpl>', s, '</tpl>'].join('');
37687
37688     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
37689
37690     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
37691     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
37692     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
37693     var m, id = 0;
37694     var tpls = [];
37695
37696     while(m = s.match(re)){
37697        var m2 = m[0].match(nameRe);
37698        var m3 = m[0].match(ifRe);
37699        var m4 = m[0].match(execRe);
37700        var exp = null, fn = null, exec = null;
37701        var name = m2 && m2[1] ? m2[1] : '';
37702        if(m3){
37703            exp = m3 && m3[1] ? m3[1] : null;
37704            if(exp){
37705                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
37706            }
37707        }
37708        if(m4){
37709            exp = m4 && m4[1] ? m4[1] : null;
37710            if(exp){
37711                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
37712            }
37713        }
37714        if(name){
37715            switch(name){
37716                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
37717                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
37718                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
37719            }
37720        }
37721        tpls.push({
37722             id: id,
37723             target: name,
37724             exec: exec,
37725             test: fn,
37726             body: m[1]||''
37727         });
37728        s = s.replace(m[0], '{xtpl'+ id + '}');
37729        ++id;
37730     }
37731     for(var i = tpls.length-1; i >= 0; --i){
37732         this.compileTpl(tpls[i]);
37733     }
37734     this.master = tpls[tpls.length-1];
37735     this.tpls = tpls;
37736 };
37737 Roo.extend(Roo.XTemplate, Roo.Template, {
37738
37739     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
37740
37741     applySubTemplate : function(id, values, parent){
37742         var t = this.tpls[id];
37743         if(t.test && !t.test.call(this, values, parent)){
37744             return '';
37745         }
37746         if(t.exec && t.exec.call(this, values, parent)){
37747             return '';
37748         }
37749         var vs = t.target ? t.target.call(this, values, parent) : values;
37750         parent = t.target ? values : parent;
37751         if(t.target && vs instanceof Array){
37752             var buf = [];
37753             for(var i = 0, len = vs.length; i < len; i++){
37754                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
37755             }
37756             return buf.join('');
37757         }
37758         return t.compiled.call(this, vs, parent);
37759     },
37760
37761     compileTpl : function(tpl){
37762         var fm = Roo.util.Format;
37763         var useF = this.disableFormats !== true;
37764         var sep = Roo.isGecko ? "+" : ",";
37765         var fn = function(m, name, format, args){
37766             if(name.substr(0, 4) == 'xtpl'){
37767                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
37768             }
37769             var v;
37770             if(name.indexOf('.') != -1){
37771                 v = name;
37772             }else{
37773                 v = "values['" + name + "']";
37774             }
37775             if(format && useF){
37776                 args = args ? ',' + args : "";
37777                 if(format.substr(0, 5) != "this."){
37778                     format = "fm." + format + '(';
37779                 }else{
37780                     format = 'this.call("'+ format.substr(5) + '", ';
37781                     args = ", values";
37782                 }
37783             }else{
37784                 args= ''; format = "("+v+" === undefined ? '' : ";
37785             }
37786             return "'"+ sep + format + v + args + ")"+sep+"'";
37787         };
37788         var body;
37789         // branched to use + in gecko and [].join() in others
37790         if(Roo.isGecko){
37791             body = "tpl.compiled = function(values, parent){ return '" +
37792                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
37793                     "';};";
37794         }else{
37795             body = ["tpl.compiled = function(values, parent){ return ['"];
37796             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
37797             body.push("'].join('');};");
37798             body = body.join('');
37799         }
37800         /** eval:var:zzzzzzz */
37801         eval(body);
37802         return this;
37803     },
37804
37805     applyTemplate : function(values){
37806         return this.master.compiled.call(this, values, {});
37807         var s = this.subs;
37808     },
37809
37810     apply : function(){
37811         return this.applyTemplate.apply(this, arguments);
37812     },
37813
37814     compile : function(){return this;}
37815 });
37816
37817 Roo.XTemplate.from = function(el){
37818     el = Roo.getDom(el);
37819     return new Roo.XTemplate(el.value || el.innerHTML);
37820 };/*
37821  * Original code for Roojs - LGPL
37822  * <script type="text/javascript">
37823  */
37824  
37825 /**
37826  * @class Roo.XComponent
37827  * A delayed Element creator...
37828  * Or a way to group chunks of interface together.
37829  * 
37830  * Mypart.xyx = new Roo.XComponent({
37831
37832     parent : 'Mypart.xyz', // empty == document.element.!!
37833     order : '001',
37834     name : 'xxxx'
37835     region : 'xxxx'
37836     disabled : function() {} 
37837      
37838     tree : function() { // return an tree of xtype declared components
37839         var MODULE = this;
37840         return 
37841         {
37842             xtype : 'NestedLayoutPanel',
37843             // technicall
37844         }
37845      ]
37846  *})
37847  *
37848  *
37849  * It can be used to build a big heiracy, with parent etc.
37850  * or you can just use this to render a single compoent to a dom element
37851  * MYPART.render(Roo.Element | String(id) | dom_element )
37852  * 
37853  * @extends Roo.util.Observable
37854  * @constructor
37855  * @param cfg {Object} configuration of component
37856  * 
37857  */
37858 Roo.XComponent = function(cfg) {
37859     Roo.apply(this, cfg);
37860     this.addEvents({ 
37861         /**
37862              * @event built
37863              * Fires when this the componnt is built
37864              * @param {Roo.XComponent} c the component
37865              */
37866         'built' : true,
37867         /**
37868              * @event buildcomplete
37869              * Fires on the top level element when all elements have been built
37870              * @param {Roo.XComponent} c the top level component.
37871          */
37872         'buildcomplete' : true
37873         
37874     });
37875     this.region = this.region || 'center'; // default..
37876     Roo.XComponent.register(this);
37877     this.modules = false;
37878     this.el = false; // where the layout goes..
37879     
37880     
37881 }
37882 Roo.extend(Roo.XComponent, Roo.util.Observable, {
37883     /**
37884      * @property el
37885      * The created element (with Roo.factory())
37886      * @type {Roo.Layout}
37887      */
37888     el  : false,
37889     
37890     /**
37891      * @property el
37892      * for BC  - use el in new code
37893      * @type {Roo.Layout}
37894      */
37895     panel : false,
37896     
37897     /**
37898      * @property layout
37899      * for BC  - use el in new code
37900      * @type {Roo.Layout}
37901      */
37902     layout : false,
37903     
37904      /**
37905      * @cfg {Function|boolean} disabled
37906      * If this module is disabled by some rule, return true from the funtion
37907      */
37908     disabled : false,
37909     
37910     /**
37911      * @cfg {String} parent 
37912      * Name of parent element which it get xtype added to..
37913      */
37914     parent: false,
37915     
37916     /**
37917      * @cfg {String} order
37918      * Used to set the order in which elements are created (usefull for multiple tabs)
37919      */
37920     
37921     order : false,
37922     /**
37923      * @cfg {String} name
37924      * String to display while loading.
37925      */
37926     name : false,
37927     /**
37928      * @cfg {String} region
37929      * Region to render component to (defaults to center)
37930      */
37931     region : 'center',
37932     
37933     /**
37934      * @cfg {Array} items
37935      * A single item array - the first element is the root of the tree..
37936      * It's done this way to stay compatible with the Xtype system...
37937      */
37938     items : false,
37939     
37940     
37941      /**
37942      * render
37943      * render element to dom or tree
37944      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
37945      */
37946     
37947     render : function(el)
37948     {
37949         
37950         el = el || false;
37951         var hp = this.parent ? 1 : 0;
37952         
37953         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
37954             // if parent is a '#.....' string, then let's use that..
37955             var ename = this.parent.substr(1)
37956             this.parent = false;
37957             el = Roo.get(ename);
37958             if (!el) {
37959                 Roo.log("Warning - element can not be found :#" + ename );
37960                 return;
37961             }
37962         }
37963         
37964         
37965         if (!this.parent) {
37966             
37967             el = el ? Roo.get(el) : false;
37968             
37969             // it's a top level one..
37970             this.parent =  {
37971                 el : new Roo.BorderLayout(el || document.body, {
37972                 
37973                      center: {
37974                          titlebar: false,
37975                          autoScroll:false,
37976                          closeOnTab: true,
37977                          tabPosition: 'top',
37978                           //resizeTabs: true,
37979                          alwaysShowTabs: el && hp? false :  true,
37980                          hideTabs: el || !hp ? true :  false,
37981                          minTabWidth: 140
37982                      }
37983                  })
37984             }
37985         }
37986         
37987         
37988             
37989         var tree = this.tree();
37990         tree.region = tree.region || this.region;
37991         this.el = this.parent.el.addxtype(tree);
37992         this.fireEvent('built', this);
37993         
37994         this.panel = this.el;
37995         this.layout = this.panel.layout;    
37996          
37997     }
37998     
37999 });
38000
38001 Roo.apply(Roo.XComponent, {
38002     
38003     /**
38004      * @property  buildCompleted
38005      * True when the builder has completed building the interface.
38006      * @type Boolean
38007      */
38008     buildCompleted : false,
38009      
38010     /**
38011      * @property  topModule
38012      * the upper most module - uses document.element as it's constructor.
38013      * @type Object
38014      */
38015      
38016     topModule  : false,
38017       
38018     /**
38019      * @property  modules
38020      * array of modules to be created by registration system.
38021      * @type {Array} of Roo.XComponent
38022      */
38023     
38024     modules : [],
38025     /**
38026      * @property  elmodules
38027      * array of modules to be created by which use #ID 
38028      * @type {Array} of Roo.XComponent
38029      */
38030      
38031     elmodules : [],
38032
38033     
38034     /**
38035      * Register components to be built later.
38036      *
38037      * This solves the following issues
38038      * - Building is not done on page load, but after an authentication process has occured.
38039      * - Interface elements are registered on page load
38040      * - Parent Interface elements may not be loaded before child, so this handles that..
38041      * 
38042      *
38043      * example:
38044      * 
38045      * MyApp.register({
38046           order : '000001',
38047           module : 'Pman.Tab.projectMgr',
38048           region : 'center',
38049           parent : 'Pman.layout',
38050           disabled : false,  // or use a function..
38051         })
38052      
38053      * * @param {Object} details about module
38054      */
38055     register : function(obj) {
38056         this.modules.push(obj);
38057          
38058     },
38059     /**
38060      * convert a string to an object..
38061      * eg. 'AAA.BBB' -> finds AAA.BBB
38062
38063      */
38064     
38065     toObject : function(str)
38066     {
38067         if (!str || typeof(str) == 'object') {
38068             return str;
38069         }
38070         if (str.substring(0,1) == '#') {
38071             return str;
38072         }
38073
38074         var ar = str.split('.');
38075         var rt, o;
38076         rt = ar.shift();
38077             /** eval:var:o */
38078         try {
38079             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
38080         } catch (e) {
38081             throw "Module not found : " + str;
38082         }
38083         
38084         if (o === false) {
38085             throw "Module not found : " + str;
38086         }
38087         Roo.each(ar, function(e) {
38088             if (typeof(o[e]) == 'undefined') {
38089                 throw "Module not found : " + str;
38090             }
38091             o = o[e];
38092         });
38093         
38094         return o;
38095         
38096     },
38097     
38098     
38099     /**
38100      * move modules into their correct place in the tree..
38101      * 
38102      */
38103     preBuild : function ()
38104     {
38105         var _t = this;
38106         Roo.each(this.modules , function (obj)
38107         {
38108             var opar = obj.parent;
38109             try { 
38110                 obj.parent = this.toObject(opar);
38111             } catch(e) {
38112                 Roo.log(e.toString());
38113                 return;
38114             }
38115             
38116             if (!obj.parent) {
38117                 this.topModule = obj;
38118                 return;
38119             }
38120             if (typeof(obj.parent) == 'string') {
38121                 this.elmodules.push(obj);
38122                 return;
38123             }
38124             if (obj.parent.constructor != Roo.XComponent) {
38125                 Roo.log("Object Parent is not instance of XComponent:" + obj.name)
38126             }
38127             if (!obj.parent.modules) {
38128                 obj.parent.modules = new Roo.util.MixedCollection(false, 
38129                     function(o) { return o.order + '' }
38130                 );
38131             }
38132             
38133             obj.parent.modules.add(obj);
38134         }, this);
38135     },
38136     
38137      /**
38138      * make a list of modules to build.
38139      * @return {Array} list of modules. 
38140      */ 
38141     
38142     buildOrder : function()
38143     {
38144         var _this = this;
38145         var cmp = function(a,b) {   
38146             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
38147         };
38148         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
38149             throw "No top level modules to build";
38150         }
38151         
38152         // make a flat list in order of modules to build.
38153         var mods = this.topModule ? [ this.topModule ] : [];
38154         Roo.each(this.elmodules,function(e) { mods.push(e) });
38155
38156         
38157         // add modules to their parents..
38158         var addMod = function(m) {
38159            // Roo.debug && Roo.log(m.modKey);
38160             
38161             mods.push(m);
38162             if (m.modules) {
38163                 m.modules.keySort('ASC',  cmp );
38164                 m.modules.each(addMod);
38165             }
38166             // not sure if this is used any more..
38167             if (m.finalize) {
38168                 m.finalize.name = m.name + " (clean up) ";
38169                 mods.push(m.finalize);
38170             }
38171             
38172         }
38173         if (this.topModule) { 
38174             this.topModule.modules.keySort('ASC',  cmp );
38175             this.topModule.modules.each(addMod);
38176         }
38177         return mods;
38178     },
38179     
38180      /**
38181      * Build the registered modules.
38182      * @param {Object} parent element.
38183      * @param {Function} optional method to call after module has been added.
38184      * 
38185      */ 
38186    
38187     build : function() 
38188     {
38189         
38190         this.preBuild();
38191         var mods = this.buildOrder();
38192       
38193         //this.allmods = mods;
38194         //Roo.debug && Roo.log(mods);
38195         //return;
38196         if (!mods.length) { // should not happen
38197             throw "NO modules!!!";
38198         }
38199         
38200         
38201         
38202         // flash it up as modal - so we store the mask!?
38203         Roo.MessageBox.show({ title: 'loading' });
38204         Roo.MessageBox.show({
38205            title: "Please wait...",
38206            msg: "Building Interface...",
38207            width:450,
38208            progress:true,
38209            closable:false,
38210            modal: false
38211           
38212         });
38213         var total = mods.length;
38214         
38215         var _this = this;
38216         var progressRun = function() {
38217             if (!mods.length) {
38218                 Roo.debug && Roo.log('hide?');
38219                 Roo.MessageBox.hide();
38220                 if (_this.topModule) { 
38221                     _this.topModule.fireEvent('buildcomplete', _this.topModule);
38222                 }
38223                 // THE END...
38224                 return false;   
38225             }
38226             
38227             var m = mods.shift();
38228             
38229             
38230             Roo.debug && Roo.log(m);
38231             // not sure if this is supported any more.. - modules that are are just function
38232             if (typeof(m) == 'function') { 
38233                 m.call(this);
38234                 return progressRun.defer(10, _this);
38235             } 
38236             
38237             
38238             
38239             Roo.MessageBox.updateProgress(
38240                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
38241                     " of " + total + 
38242                     (m.name ? (' - ' + m.name) : '')
38243                     );
38244             
38245          
38246             // is the module disabled?
38247             var disabled = (typeof(m.disabled) == 'function') ?
38248                 m.disabled.call(m.module.disabled) : m.disabled;    
38249             
38250             
38251             if (disabled) {
38252                 return progressRun(); // we do not update the display!
38253             }
38254             
38255             // now build 
38256             
38257             m.render();
38258             // it's 10 on top level, and 1 on others??? why...
38259             return progressRun.defer(10, _this);
38260              
38261         }
38262         progressRun.defer(1, _this);
38263      
38264         
38265         
38266     }
38267     
38268      
38269    
38270     
38271     
38272 });
38273  //<script type="text/javascript">
38274
38275
38276 /**
38277  * @class Roo.Login
38278  * @extends Roo.LayoutDialog
38279  * A generic Login Dialog..... - only one needed in theory!?!?
38280  *
38281  * Fires XComponent builder on success...
38282  * 
38283  * Sends 
38284  *    username,password, lang = for login actions.
38285  *    check = 1 for periodic checking that sesion is valid.
38286  *    passwordRequest = email request password
38287  *    logout = 1 = to logout
38288  * 
38289  * Affects: (this id="????" elements)
38290  *   loading  (removed) (used to indicate application is loading)
38291  *   loading-mask (hides) (used to hide application when it's building loading)
38292  *   
38293  * 
38294  * Usage: 
38295  *    
38296  * 
38297  * Myapp.login = Roo.Login({
38298      url: xxxx,
38299    
38300      realm : 'Myapp', 
38301      
38302      
38303      method : 'POST',
38304      
38305      
38306      * 
38307  })
38308  * 
38309  * 
38310  * 
38311  **/
38312  
38313 Roo.Login = function(cfg)
38314 {
38315     this.addEvents({
38316         'refreshed' : true
38317     });
38318     
38319     Roo.apply(this,cfg);
38320     
38321     Roo.onReady(function() {
38322         this.onLoad();
38323     }, this);
38324     // call parent..
38325     
38326    
38327     Roo.Login.superclass.constructor.call(this, this);
38328     //this.addxtype(this.items[0]);
38329     
38330     
38331 }
38332
38333
38334 Roo.extend(Roo.Login, Roo.LayoutDialog, {
38335     
38336     /**
38337      * @cfg {String} method
38338      * Method used to query for login details.
38339      */
38340     
38341     method : 'POST',
38342     /**
38343      * @cfg {String} url
38344      * URL to query login data. - eg. baseURL + '/Login.php'
38345      */
38346     url : '',
38347     
38348     /**
38349      * @property user
38350      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
38351      * @type {Object} 
38352      */
38353     user : false,
38354     /**
38355      * @property checkFails
38356      * Number of times we have attempted to get authentication check, and failed.
38357      * @type {Number} 
38358      */
38359     checkFails : 0,
38360       /**
38361      * @property intervalID
38362      * The window interval that does the constant login checking.
38363      * @type {Number} 
38364      */
38365     intervalID : 0,
38366     
38367     
38368     onLoad : function() // called on page load...
38369     {
38370         // load 
38371          
38372         if (Roo.get('loading')) { // clear any loading indicator..
38373             Roo.get('loading').remove();
38374         }
38375         
38376         //this.switchLang('en'); // set the language to english..
38377        
38378         this.check({
38379             success:  function(response, opts)  {  // check successfull...
38380             
38381                 var res = this.processResponse(response);
38382                 this.checkFails =0;
38383                 if (!res.success) { // error!
38384                     this.checkFails = 5;
38385                     //console.log('call failure');
38386                     return this.failure(response,opts);
38387                 }
38388                 
38389                 if (!res.data.id) { // id=0 == login failure.
38390                     return this.show();
38391                 }
38392                 
38393                               
38394                         //console.log(success);
38395                 this.fillAuth(res.data);   
38396                 this.checkFails =0;
38397                 Roo.XComponent.build();
38398             },
38399             failure : this.show
38400         });
38401         
38402     }, 
38403     
38404     
38405     check: function(cfg) // called every so often to refresh cookie etc..
38406     {
38407         if (cfg.again) { // could be undefined..
38408             this.checkFails++;
38409         } else {
38410             this.checkFails = 0;
38411         }
38412         var _this = this;
38413         if (this.sending) {
38414             if ( this.checkFails > 4) {
38415                 Roo.MessageBox.alert("Error",  
38416                     "Error getting authentication status. - try reloading, or wait a while", function() {
38417                         _this.sending = false;
38418                     }); 
38419                 return;
38420             }
38421             cfg.again = true;
38422             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
38423             return;
38424         }
38425         this.sending = true;
38426         
38427         Roo.Ajax.request({  
38428             url: this.url,
38429             params: {
38430                 getAuthUser: true
38431             },  
38432             method: this.method,
38433             success:  cfg.success || this.success,
38434             failure : cfg.failure || this.failure,
38435             scope : this,
38436             callCfg : cfg
38437               
38438         });  
38439     }, 
38440     
38441     
38442     logout: function()
38443     {
38444         window.onbeforeunload = function() { }; // false does not work for IE..
38445         this.user = false;
38446         var _this = this;
38447         
38448         Roo.Ajax.request({  
38449             url: this.url,
38450             params: {
38451                 logout: 1
38452             },  
38453             method: 'GET',
38454             failure : function() {
38455                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
38456                     document.location = document.location.toString() + '?ts=' + Math.random();
38457                 });
38458                 
38459             },
38460             success : function() {
38461                 _this.user = false;
38462                 this.checkFails =0;
38463                 // fixme..
38464                 document.location = document.location.toString() + '?ts=' + Math.random();
38465             }
38466               
38467               
38468         }); 
38469     },
38470     
38471     processResponse : function (response)
38472     {
38473         var res = '';
38474         try {
38475             res = Roo.decode(response.responseText);
38476             // oops...
38477             if (typeof(res) != 'object') {
38478                 res = { success : false, errorMsg : res, errors : true };
38479             }
38480             if (typeof(res.success) == 'undefined') {
38481                 res.success = false;
38482             }
38483             
38484         } catch(e) {
38485             res = { success : false,  errorMsg : response.responseText, errors : true };
38486         }
38487         return res;
38488     },
38489     
38490     success : function(response, opts)  // check successfull...
38491     {  
38492         this.sending = false;
38493         var res = this.processResponse(response);
38494         if (!res.success) {
38495             return this.failure(response, opts);
38496         }
38497         if (!res.data || !res.data.id) {
38498             return this.failure(response,opts);
38499         }
38500         //console.log(res);
38501         this.fillAuth(res.data);
38502         
38503         this.checkFails =0;
38504         
38505     },
38506     
38507     
38508     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
38509     {
38510         this.authUser = -1;
38511         this.sending = false;
38512         var res = this.processResponse(response);
38513         //console.log(res);
38514         if ( this.checkFails > 2) {
38515         
38516             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
38517                 "Error getting authentication status. - try reloading"); 
38518             return;
38519         }
38520         opts.callCfg.again = true;
38521         this.check.defer(1000, this, [ opts.callCfg ]);
38522         return;  
38523     },
38524     
38525     
38526     
38527     fillAuth: function(au) {
38528         this.startAuthCheck();
38529         this.authUserId = au.id;
38530         this.authUser = au;
38531         this.lastChecked = new Date();
38532         this.fireEvent('refreshed', au);
38533         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
38534         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
38535         au.lang = au.lang || 'en';
38536         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
38537         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
38538         this.switchLang(au.lang );
38539         
38540      
38541         // open system... - -on setyp..
38542         if (this.authUserId  < 0) {
38543             Roo.MessageBox.alert("Warning", 
38544                 "This is an open system - please set up a admin user with a password.");  
38545         }
38546          
38547         //Pman.onload(); // which should do nothing if it's a re-auth result...
38548         
38549              
38550     },
38551     
38552     startAuthCheck : function() // starter for timeout checking..
38553     {
38554         if (this.intervalID) { // timer already in place...
38555             return false;
38556         }
38557         var _this = this;
38558         this.intervalID =  window.setInterval(function() {
38559               _this.check(false);
38560             }, 120000); // every 120 secs = 2mins..
38561         
38562         
38563     },
38564          
38565     
38566     switchLang : function (lang) 
38567     {
38568         _T = typeof(_T) == 'undefined' ? false : _T;
38569           if (!_T || !lang.length) {
38570             return;
38571         }
38572         
38573         if (!_T && lang != 'en') {
38574             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
38575             return;
38576         }
38577         
38578         if (typeof(_T.en) == 'undefined') {
38579             _T.en = {};
38580             Roo.apply(_T.en, _T);
38581         }
38582         
38583         if (typeof(_T[lang]) == 'undefined') {
38584             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
38585             return;
38586         }
38587         
38588         
38589         Roo.apply(_T, _T[lang]);
38590         // just need to set the text values for everything...
38591         var _this = this;
38592         /* this will not work ...
38593         if (this.form) { 
38594             
38595                
38596             function formLabel(name, val) {
38597                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
38598             }
38599             
38600             formLabel('password', "Password"+':');
38601             formLabel('username', "Email Address"+':');
38602             formLabel('lang', "Language"+':');
38603             this.dialog.setTitle("Login");
38604             this.dialog.buttons[0].setText("Forgot Password");
38605             this.dialog.buttons[1].setText("Login");
38606         }
38607         */
38608         
38609         
38610     },
38611     
38612     
38613     title: "Login",
38614     modal: true,
38615     width:  350,
38616     //height: 230,
38617     height: 180,
38618     shadow: true,
38619     minWidth:200,
38620     minHeight:180,
38621     //proxyDrag: true,
38622     closable: false,
38623     draggable: false,
38624     collapsible: false,
38625     resizable: false,
38626     center: {  // needed??
38627         autoScroll:false,
38628         titlebar: false,
38629        // tabPosition: 'top',
38630         hideTabs: true,
38631         closeOnTab: true,
38632         alwaysShowTabs: false
38633     } ,
38634     listeners : {
38635         
38636         show  : function(dlg)
38637         {
38638             //console.log(this);
38639             this.form = this.layout.getRegion('center').activePanel.form;
38640             this.form.dialog = dlg;
38641             this.buttons[0].form = this.form;
38642             this.buttons[0].dialog = dlg;
38643             this.buttons[1].form = this.form;
38644             this.buttons[1].dialog = dlg;
38645            
38646            //this.resizeToLogo.defer(1000,this);
38647             // this is all related to resizing for logos..
38648             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
38649            //// if (!sz) {
38650              //   this.resizeToLogo.defer(1000,this);
38651              //   return;
38652            // }
38653             //var w = Ext.lib.Dom.getViewWidth() - 100;
38654             //var h = Ext.lib.Dom.getViewHeight() - 100;
38655             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
38656             //this.center();
38657             if (this.disabled) {
38658                 this.hide();
38659                 return;
38660             }
38661             
38662             if (this.user.id < 0) { // used for inital setup situations.
38663                 return;
38664             }
38665             
38666             if (this.intervalID) {
38667                 // remove the timer
38668                 window.clearInterval(this.intervalID);
38669                 this.intervalID = false;
38670             }
38671             
38672             
38673             if (Roo.get('loading')) {
38674                 Roo.get('loading').remove();
38675             }
38676             if (Roo.get('loading-mask')) {
38677                 Roo.get('loading-mask').hide();
38678             }
38679             
38680             //incomming._node = tnode;
38681             this.form.reset();
38682             //this.dialog.modal = !modal;
38683             //this.dialog.show();
38684             this.el.unmask(); 
38685             
38686             
38687             this.form.setValues({
38688                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
38689                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
38690             });
38691             
38692             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
38693             if (this.form.findField('username').getValue().length > 0 ){
38694                 this.form.findField('password').focus();
38695             } else {
38696                this.form.findField('username').focus();
38697             }
38698     
38699         }
38700     },
38701     items : [
38702          {
38703        
38704             xtype : 'ContentPanel',
38705             xns : Roo,
38706             region: 'center',
38707             fitToFrame : true,
38708             
38709             items : [
38710     
38711                 {
38712                
38713                     xtype : 'Form',
38714                     xns : Roo.form,
38715                     labelWidth: 100,
38716                     style : 'margin: 10px;',
38717                     
38718                     listeners : {
38719                         actionfailed : function(f, act) {
38720                             // form can return { errors: .... }
38721                                 
38722                             //act.result.errors // invalid form element list...
38723                             //act.result.errorMsg// invalid form element list...
38724                             
38725                             this.dialog.el.unmask();
38726                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
38727                                         "Login failed - communication error - try again.");
38728                                       
38729                         },
38730                         actioncomplete: function(re, act) {
38731                              
38732                             Roo.state.Manager.set(
38733                                 this.dialog.realm + '.username',  
38734                                     this.findField('username').getValue()
38735                             );
38736                             Roo.state.Manager.set(
38737                                 this.dialog.realm + '.lang',  
38738                                 this.findField('lang').getValue() 
38739                             );
38740                             
38741                             this.dialog.fillAuth(act.result.data);
38742                               
38743                             this.dialog.hide();
38744                             
38745                             if (Roo.get('loading-mask')) {
38746                                 Roo.get('loading-mask').show();
38747                             }
38748                             Roo.XComponent.build();
38749                             
38750                              
38751                             
38752                         }
38753                     },
38754                     items : [
38755                         {
38756                             xtype : 'TextField',
38757                             xns : Roo.form,
38758                             fieldLabel: "Email Address",
38759                             name: 'username',
38760                             width:200,
38761                             autoCreate : {tag: "input", type: "text", size: "20"}
38762                         },
38763                         {
38764                             xtype : 'TextField',
38765                             xns : Roo.form,
38766                             fieldLabel: "Password",
38767                             inputType: 'password',
38768                             name: 'password',
38769                             width:200,
38770                             autoCreate : {tag: "input", type: "text", size: "20"},
38771                             listeners : {
38772                                 specialkey : function(e,ev) {
38773                                     if (ev.keyCode == 13) {
38774                                         this.form.dialog.el.mask("Logging in");
38775                                         this.form.doAction('submit', {
38776                                             url: this.form.dialog.url,
38777                                             method: this.form.dialog.method
38778                                         });
38779                                     }
38780                                 }
38781                             }  
38782                         },
38783                         {
38784                             xtype : 'ComboBox',
38785                             xns : Roo.form,
38786                             fieldLabel: "Language",
38787                             name : 'langdisp',
38788                             store: {
38789                                 xtype : 'SimpleStore',
38790                                 fields: ['lang', 'ldisp'],
38791                                 data : [
38792                                     [ 'en', 'English' ],
38793                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
38794                                     [ 'zh_CN', '\u7C21\u4E2D' ]
38795                                 ]
38796                             },
38797                             
38798                             valueField : 'lang',
38799                             hiddenName:  'lang',
38800                             width: 200,
38801                             displayField:'ldisp',
38802                             typeAhead: false,
38803                             editable: false,
38804                             mode: 'local',
38805                             triggerAction: 'all',
38806                             emptyText:'Select a Language...',
38807                             selectOnFocus:true,
38808                             listeners : {
38809                                 select :  function(cb, rec, ix) {
38810                                     this.form.switchLang(rec.data.lang);
38811                                 }
38812                             }
38813                         
38814                         }
38815                     ]
38816                 }
38817                   
38818                 
38819             ]
38820         }
38821     ],
38822     buttons : [
38823         {
38824             xtype : 'Button',
38825             xns : 'Roo',
38826             text : "Forgot Password",
38827             listeners : {
38828                 click : function() {
38829                     //console.log(this);
38830                     var n = this.form.findField('username').getValue();
38831                     if (!n.length) {
38832                         Roo.MessageBox.alert("Error", "Fill in your email address");
38833                         return;
38834                     }
38835                     Roo.Ajax.request({
38836                         url: this.dialog.url,
38837                         params: {
38838                             passwordRequest: n
38839                         },
38840                         method: this.dialog.method,
38841                         success:  function(response, opts)  {  // check successfull...
38842                         
38843                             var res = this.dialog.processResponse(response);
38844                             if (!res.success) { // error!
38845                                Roo.MessageBox.alert("Error" ,
38846                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
38847                                return;
38848                             }
38849                             Roo.MessageBox.alert("Notice" ,
38850                                 "Please check you email for the Password Reset message");
38851                         },
38852                         failure : function() {
38853                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
38854                         }
38855                         
38856                     });
38857                 }
38858             }
38859         },
38860         {
38861             xtype : 'Button',
38862             xns : 'Roo',
38863             text : "Login",
38864             listeners : {
38865                 
38866                 click : function () {
38867                         
38868                     this.dialog.el.mask("Logging in");
38869                     this.form.doAction('submit', {
38870                             url: this.dialog.url,
38871                             method: this.dialog.method
38872                     });
38873                 }
38874             }
38875         }
38876     ]
38877   
38878   
38879 })
38880  
38881
38882
38883