fix html editor
[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             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
15649                         Math.max(opt.minWidth || this.minWidth, bwidth));
15650             if(opt.prompt){
15651                 activeTextEl.setWidth(w);
15652             }
15653             if(dlg.isVisible()){
15654                 dlg.fixedcenter = false;
15655             }
15656             // to big, make it scoll.
15657             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
15658                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
15659                 bodyEl.dom.style.overflowY = 'auto';
15660             } else {
15661                 bodyEl.dom.style.height = '';
15662                 bodyEl.dom.style.overflowY = '';
15663             }
15664             
15665             dlg.setContentSize(w, bodyEl.getHeight());
15666             if(dlg.isVisible()){
15667                 dlg.fixedcenter = true;
15668             }
15669             return this;
15670         },
15671
15672         /**
15673          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15674          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15675          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15676          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15677          * @return {Roo.MessageBox} This message box
15678          */
15679         updateProgress : function(value, text){
15680             if(text){
15681                 this.updateText(text);
15682             }
15683             if (pp) { // weird bug on my firefox - for some reason this is not defined
15684                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15685             }
15686             return this;
15687         },        
15688
15689         /**
15690          * Returns true if the message box is currently displayed
15691          * @return {Boolean} True if the message box is visible, else false
15692          */
15693         isVisible : function(){
15694             return dlg && dlg.isVisible();  
15695         },
15696
15697         /**
15698          * Hides the message box if it is displayed
15699          */
15700         hide : function(){
15701             if(this.isVisible()){
15702                 dlg.hide();
15703             }  
15704         },
15705
15706         /**
15707          * Displays a new message box, or reinitializes an existing message box, based on the config options
15708          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15709          * The following config object properties are supported:
15710          * <pre>
15711 Property    Type             Description
15712 ----------  ---------------  ------------------------------------------------------------------------------------
15713 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15714                                    closes (defaults to undefined)
15715 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15716                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15717 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15718                                    progress and wait dialogs will ignore this property and always hide the
15719                                    close button as they can only be closed programmatically.
15720 cls               String           A custom CSS class to apply to the message box element
15721 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15722                                    displayed (defaults to 75)
15723 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15724                                    function will be btn (the name of the button that was clicked, if applicable,
15725                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15726                                    Progress and wait dialogs will ignore this option since they do not respond to
15727                                    user actions and can only be closed programmatically, so any required function
15728                                    should be called by the same code after it closes the dialog.
15729 icon              String           A CSS class that provides a background image to be used as an icon for
15730                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15731 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15732 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15733 modal             Boolean          False to allow user interaction with the page while the message box is
15734                                    displayed (defaults to true)
15735 msg               String           A string that will replace the existing message box body text (defaults
15736                                    to the XHTML-compliant non-breaking space character '&#160;')
15737 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15738 progress          Boolean          True to display a progress bar (defaults to false)
15739 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15740 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15741 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15742 title             String           The title text
15743 value             String           The string value to set into the active textbox element if displayed
15744 wait              Boolean          True to display a progress bar (defaults to false)
15745 width             Number           The width of the dialog in pixels
15746 </pre>
15747          *
15748          * Example usage:
15749          * <pre><code>
15750 Roo.Msg.show({
15751    title: 'Address',
15752    msg: 'Please enter your address:',
15753    width: 300,
15754    buttons: Roo.MessageBox.OKCANCEL,
15755    multiline: true,
15756    fn: saveAddress,
15757    animEl: 'addAddressBtn'
15758 });
15759 </code></pre>
15760          * @param {Object} config Configuration options
15761          * @return {Roo.MessageBox} This message box
15762          */
15763         show : function(options)
15764         {
15765             
15766             // this causes nightmares if you show one dialog after another
15767             // especially on callbacks..
15768              
15769             if(this.isVisible()){
15770                 
15771                 this.hide();
15772                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML )
15773                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
15774                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
15775                 
15776             }
15777             var d = this.getDialog();
15778             opt = options;
15779             d.setTitle(opt.title || "&#160;");
15780             d.close.setDisplayed(opt.closable !== false);
15781             activeTextEl = textboxEl;
15782             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15783             if(opt.prompt){
15784                 if(opt.multiline){
15785                     textboxEl.hide();
15786                     textareaEl.show();
15787                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15788                         opt.multiline : this.defaultTextHeight);
15789                     activeTextEl = textareaEl;
15790                 }else{
15791                     textboxEl.show();
15792                     textareaEl.hide();
15793                 }
15794             }else{
15795                 textboxEl.hide();
15796                 textareaEl.hide();
15797             }
15798             progressEl.setDisplayed(opt.progress === true);
15799             this.updateProgress(0);
15800             activeTextEl.dom.value = opt.value || "";
15801             if(opt.prompt){
15802                 dlg.setDefaultButton(activeTextEl);
15803             }else{
15804                 var bs = opt.buttons;
15805                 var db = null;
15806                 if(bs && bs.ok){
15807                     db = buttons["ok"];
15808                 }else if(bs && bs.yes){
15809                     db = buttons["yes"];
15810                 }
15811                 dlg.setDefaultButton(db);
15812             }
15813             bwidth = updateButtons(opt.buttons);
15814             this.updateText(opt.msg);
15815             if(opt.cls){
15816                 d.el.addClass(opt.cls);
15817             }
15818             d.proxyDrag = opt.proxyDrag === true;
15819             d.modal = opt.modal !== false;
15820             d.mask = opt.modal !== false ? mask : false;
15821             if(!d.isVisible()){
15822                 // force it to the end of the z-index stack so it gets a cursor in FF
15823                 document.body.appendChild(dlg.el.dom);
15824                 d.animateTarget = null;
15825                 d.show(options.animEl);
15826             }
15827             return this;
15828         },
15829
15830         /**
15831          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15832          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15833          * and closing the message box when the process is complete.
15834          * @param {String} title The title bar text
15835          * @param {String} msg The message box body text
15836          * @return {Roo.MessageBox} This message box
15837          */
15838         progress : function(title, msg){
15839             this.show({
15840                 title : title,
15841                 msg : msg,
15842                 buttons: false,
15843                 progress:true,
15844                 closable:false,
15845                 minWidth: this.minProgressWidth,
15846                 modal : true
15847             });
15848             return this;
15849         },
15850
15851         /**
15852          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15853          * If a callback function is passed it will be called after the user clicks the button, and the
15854          * id of the button that was clicked will be passed as the only parameter to the callback
15855          * (could also be the top-right close button).
15856          * @param {String} title The title bar text
15857          * @param {String} msg The message box body text
15858          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15859          * @param {Object} scope (optional) The scope of the callback function
15860          * @return {Roo.MessageBox} This message box
15861          */
15862         alert : function(title, msg, fn, scope){
15863             this.show({
15864                 title : title,
15865                 msg : msg,
15866                 buttons: this.OK,
15867                 fn: fn,
15868                 scope : scope,
15869                 modal : true
15870             });
15871             return this;
15872         },
15873
15874         /**
15875          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15876          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15877          * You are responsible for closing the message box when the process is complete.
15878          * @param {String} msg The message box body text
15879          * @param {String} title (optional) The title bar text
15880          * @return {Roo.MessageBox} This message box
15881          */
15882         wait : function(msg, title){
15883             this.show({
15884                 title : title,
15885                 msg : msg,
15886                 buttons: false,
15887                 closable:false,
15888                 progress:true,
15889                 modal:true,
15890                 width:300,
15891                 wait:true
15892             });
15893             waitTimer = Roo.TaskMgr.start({
15894                 run: function(i){
15895                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15896                 },
15897                 interval: 1000
15898             });
15899             return this;
15900         },
15901
15902         /**
15903          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15904          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15905          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15906          * @param {String} title The title bar text
15907          * @param {String} msg The message box body text
15908          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15909          * @param {Object} scope (optional) The scope of the callback function
15910          * @return {Roo.MessageBox} This message box
15911          */
15912         confirm : function(title, msg, fn, scope){
15913             this.show({
15914                 title : title,
15915                 msg : msg,
15916                 buttons: this.YESNO,
15917                 fn: fn,
15918                 scope : scope,
15919                 modal : true
15920             });
15921             return this;
15922         },
15923
15924         /**
15925          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15926          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15927          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15928          * (could also be the top-right close button) and the text that was entered will be passed as the two
15929          * parameters to the callback.
15930          * @param {String} title The title bar text
15931          * @param {String} msg The message box body text
15932          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15933          * @param {Object} scope (optional) The scope of the callback function
15934          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15935          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15936          * @return {Roo.MessageBox} This message box
15937          */
15938         prompt : function(title, msg, fn, scope, multiline){
15939             this.show({
15940                 title : title,
15941                 msg : msg,
15942                 buttons: this.OKCANCEL,
15943                 fn: fn,
15944                 minWidth:250,
15945                 scope : scope,
15946                 prompt:true,
15947                 multiline: multiline,
15948                 modal : true
15949             });
15950             return this;
15951         },
15952
15953         /**
15954          * Button config that displays a single OK button
15955          * @type Object
15956          */
15957         OK : {ok:true},
15958         /**
15959          * Button config that displays Yes and No buttons
15960          * @type Object
15961          */
15962         YESNO : {yes:true, no:true},
15963         /**
15964          * Button config that displays OK and Cancel buttons
15965          * @type Object
15966          */
15967         OKCANCEL : {ok:true, cancel:true},
15968         /**
15969          * Button config that displays Yes, No and Cancel buttons
15970          * @type Object
15971          */
15972         YESNOCANCEL : {yes:true, no:true, cancel:true},
15973
15974         /**
15975          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15976          * @type Number
15977          */
15978         defaultTextHeight : 75,
15979         /**
15980          * The maximum width in pixels of the message box (defaults to 600)
15981          * @type Number
15982          */
15983         maxWidth : 600,
15984         /**
15985          * The minimum width in pixels of the message box (defaults to 100)
15986          * @type Number
15987          */
15988         minWidth : 100,
15989         /**
15990          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
15991          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
15992          * @type Number
15993          */
15994         minProgressWidth : 250,
15995         /**
15996          * An object containing the default button text strings that can be overriden for localized language support.
15997          * Supported properties are: ok, cancel, yes and no.
15998          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
15999          * @type Object
16000          */
16001         buttonText : {
16002             ok : "OK",
16003             cancel : "Cancel",
16004             yes : "Yes",
16005             no : "No"
16006         }
16007     };
16008 }();
16009
16010 /**
16011  * Shorthand for {@link Roo.MessageBox}
16012  */
16013 Roo.Msg = Roo.MessageBox;/*
16014  * Based on:
16015  * Ext JS Library 1.1.1
16016  * Copyright(c) 2006-2007, Ext JS, LLC.
16017  *
16018  * Originally Released Under LGPL - original licence link has changed is not relivant.
16019  *
16020  * Fork - LGPL
16021  * <script type="text/javascript">
16022  */
16023 /**
16024  * @class Roo.QuickTips
16025  * Provides attractive and customizable tooltips for any element.
16026  * @singleton
16027  */
16028 Roo.QuickTips = function(){
16029     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
16030     var ce, bd, xy, dd;
16031     var visible = false, disabled = true, inited = false;
16032     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
16033     
16034     var onOver = function(e){
16035         if(disabled){
16036             return;
16037         }
16038         var t = e.getTarget();
16039         if(!t || t.nodeType !== 1 || t == document || t == document.body){
16040             return;
16041         }
16042         if(ce && t == ce.el){
16043             clearTimeout(hideProc);
16044             return;
16045         }
16046         if(t && tagEls[t.id]){
16047             tagEls[t.id].el = t;
16048             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
16049             return;
16050         }
16051         var ttp, et = Roo.fly(t);
16052         var ns = cfg.namespace;
16053         if(tm.interceptTitles && t.title){
16054             ttp = t.title;
16055             t.qtip = ttp;
16056             t.removeAttribute("title");
16057             e.preventDefault();
16058         }else{
16059             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
16060         }
16061         if(ttp){
16062             showProc = show.defer(tm.showDelay, tm, [{
16063                 el: t, 
16064                 text: ttp, 
16065                 width: et.getAttributeNS(ns, cfg.width),
16066                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
16067                 title: et.getAttributeNS(ns, cfg.title),
16068                     cls: et.getAttributeNS(ns, cfg.cls)
16069             }]);
16070         }
16071     };
16072     
16073     var onOut = function(e){
16074         clearTimeout(showProc);
16075         var t = e.getTarget();
16076         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
16077             hideProc = setTimeout(hide, tm.hideDelay);
16078         }
16079     };
16080     
16081     var onMove = function(e){
16082         if(disabled){
16083             return;
16084         }
16085         xy = e.getXY();
16086         xy[1] += 18;
16087         if(tm.trackMouse && ce){
16088             el.setXY(xy);
16089         }
16090     };
16091     
16092     var onDown = function(e){
16093         clearTimeout(showProc);
16094         clearTimeout(hideProc);
16095         if(!e.within(el)){
16096             if(tm.hideOnClick){
16097                 hide();
16098                 tm.disable();
16099                 tm.enable.defer(100, tm);
16100             }
16101         }
16102     };
16103     
16104     var getPad = function(){
16105         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
16106     };
16107
16108     var show = function(o){
16109         if(disabled){
16110             return;
16111         }
16112         clearTimeout(dismissProc);
16113         ce = o;
16114         if(removeCls){ // in case manually hidden
16115             el.removeClass(removeCls);
16116             removeCls = null;
16117         }
16118         if(ce.cls){
16119             el.addClass(ce.cls);
16120             removeCls = ce.cls;
16121         }
16122         if(ce.title){
16123             tipTitle.update(ce.title);
16124             tipTitle.show();
16125         }else{
16126             tipTitle.update('');
16127             tipTitle.hide();
16128         }
16129         el.dom.style.width  = tm.maxWidth+'px';
16130         //tipBody.dom.style.width = '';
16131         tipBodyText.update(o.text);
16132         var p = getPad(), w = ce.width;
16133         if(!w){
16134             var td = tipBodyText.dom;
16135             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
16136             if(aw > tm.maxWidth){
16137                 w = tm.maxWidth;
16138             }else if(aw < tm.minWidth){
16139                 w = tm.minWidth;
16140             }else{
16141                 w = aw;
16142             }
16143         }
16144         //tipBody.setWidth(w);
16145         el.setWidth(parseInt(w, 10) + p);
16146         if(ce.autoHide === false){
16147             close.setDisplayed(true);
16148             if(dd){
16149                 dd.unlock();
16150             }
16151         }else{
16152             close.setDisplayed(false);
16153             if(dd){
16154                 dd.lock();
16155             }
16156         }
16157         if(xy){
16158             el.avoidY = xy[1]-18;
16159             el.setXY(xy);
16160         }
16161         if(tm.animate){
16162             el.setOpacity(.1);
16163             el.setStyle("visibility", "visible");
16164             el.fadeIn({callback: afterShow});
16165         }else{
16166             afterShow();
16167         }
16168     };
16169     
16170     var afterShow = function(){
16171         if(ce){
16172             el.show();
16173             esc.enable();
16174             if(tm.autoDismiss && ce.autoHide !== false){
16175                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16176             }
16177         }
16178     };
16179     
16180     var hide = function(noanim){
16181         clearTimeout(dismissProc);
16182         clearTimeout(hideProc);
16183         ce = null;
16184         if(el.isVisible()){
16185             esc.disable();
16186             if(noanim !== true && tm.animate){
16187                 el.fadeOut({callback: afterHide});
16188             }else{
16189                 afterHide();
16190             } 
16191         }
16192     };
16193     
16194     var afterHide = function(){
16195         el.hide();
16196         if(removeCls){
16197             el.removeClass(removeCls);
16198             removeCls = null;
16199         }
16200     };
16201     
16202     return {
16203         /**
16204         * @cfg {Number} minWidth
16205         * The minimum width of the quick tip (defaults to 40)
16206         */
16207        minWidth : 40,
16208         /**
16209         * @cfg {Number} maxWidth
16210         * The maximum width of the quick tip (defaults to 300)
16211         */
16212        maxWidth : 300,
16213         /**
16214         * @cfg {Boolean} interceptTitles
16215         * True to automatically use the element's DOM title value if available (defaults to false)
16216         */
16217        interceptTitles : false,
16218         /**
16219         * @cfg {Boolean} trackMouse
16220         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16221         */
16222        trackMouse : false,
16223         /**
16224         * @cfg {Boolean} hideOnClick
16225         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16226         */
16227        hideOnClick : true,
16228         /**
16229         * @cfg {Number} showDelay
16230         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16231         */
16232        showDelay : 500,
16233         /**
16234         * @cfg {Number} hideDelay
16235         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16236         */
16237        hideDelay : 200,
16238         /**
16239         * @cfg {Boolean} autoHide
16240         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16241         * Used in conjunction with hideDelay.
16242         */
16243        autoHide : true,
16244         /**
16245         * @cfg {Boolean}
16246         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16247         * (defaults to true).  Used in conjunction with autoDismissDelay.
16248         */
16249        autoDismiss : true,
16250         /**
16251         * @cfg {Number}
16252         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16253         */
16254        autoDismissDelay : 5000,
16255        /**
16256         * @cfg {Boolean} animate
16257         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16258         */
16259        animate : false,
16260
16261        /**
16262         * @cfg {String} title
16263         * Title text to display (defaults to '').  This can be any valid HTML markup.
16264         */
16265         title: '',
16266        /**
16267         * @cfg {String} text
16268         * Body text to display (defaults to '').  This can be any valid HTML markup.
16269         */
16270         text : '',
16271        /**
16272         * @cfg {String} cls
16273         * A CSS class to apply to the base quick tip element (defaults to '').
16274         */
16275         cls : '',
16276        /**
16277         * @cfg {Number} width
16278         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16279         * minWidth or maxWidth.
16280         */
16281         width : null,
16282
16283     /**
16284      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16285      * or display QuickTips in a page.
16286      */
16287        init : function(){
16288           tm = Roo.QuickTips;
16289           cfg = tm.tagConfig;
16290           if(!inited){
16291               if(!Roo.isReady){ // allow calling of init() before onReady
16292                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16293                   return;
16294               }
16295               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16296               el.fxDefaults = {stopFx: true};
16297               // maximum custom styling
16298               //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>');
16299               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>');              
16300               tipTitle = el.child('h3');
16301               tipTitle.enableDisplayMode("block");
16302               tipBody = el.child('div.x-tip-bd');
16303               tipBodyText = el.child('div.x-tip-bd-inner');
16304               //bdLeft = el.child('div.x-tip-bd-left');
16305               //bdRight = el.child('div.x-tip-bd-right');
16306               close = el.child('div.x-tip-close');
16307               close.enableDisplayMode("block");
16308               close.on("click", hide);
16309               var d = Roo.get(document);
16310               d.on("mousedown", onDown);
16311               d.on("mouseover", onOver);
16312               d.on("mouseout", onOut);
16313               d.on("mousemove", onMove);
16314               esc = d.addKeyListener(27, hide);
16315               esc.disable();
16316               if(Roo.dd.DD){
16317                   dd = el.initDD("default", null, {
16318                       onDrag : function(){
16319                           el.sync();  
16320                       }
16321                   });
16322                   dd.setHandleElId(tipTitle.id);
16323                   dd.lock();
16324               }
16325               inited = true;
16326           }
16327           this.enable(); 
16328        },
16329
16330     /**
16331      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16332      * are supported:
16333      * <pre>
16334 Property    Type                   Description
16335 ----------  ---------------------  ------------------------------------------------------------------------
16336 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16337      * </ul>
16338      * @param {Object} config The config object
16339      */
16340        register : function(config){
16341            var cs = config instanceof Array ? config : arguments;
16342            for(var i = 0, len = cs.length; i < len; i++) {
16343                var c = cs[i];
16344                var target = c.target;
16345                if(target){
16346                    if(target instanceof Array){
16347                        for(var j = 0, jlen = target.length; j < jlen; j++){
16348                            tagEls[target[j]] = c;
16349                        }
16350                    }else{
16351                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16352                    }
16353                }
16354            }
16355        },
16356
16357     /**
16358      * Removes this quick tip from its element and destroys it.
16359      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16360      */
16361        unregister : function(el){
16362            delete tagEls[Roo.id(el)];
16363        },
16364
16365     /**
16366      * Enable this quick tip.
16367      */
16368        enable : function(){
16369            if(inited && disabled){
16370                locks.pop();
16371                if(locks.length < 1){
16372                    disabled = false;
16373                }
16374            }
16375        },
16376
16377     /**
16378      * Disable this quick tip.
16379      */
16380        disable : function(){
16381           disabled = true;
16382           clearTimeout(showProc);
16383           clearTimeout(hideProc);
16384           clearTimeout(dismissProc);
16385           if(ce){
16386               hide(true);
16387           }
16388           locks.push(1);
16389        },
16390
16391     /**
16392      * Returns true if the quick tip is enabled, else false.
16393      */
16394        isEnabled : function(){
16395             return !disabled;
16396        },
16397
16398         // private
16399        tagConfig : {
16400            namespace : "ext",
16401            attribute : "qtip",
16402            width : "width",
16403            target : "target",
16404            title : "qtitle",
16405            hide : "hide",
16406            cls : "qclass"
16407        }
16408    };
16409 }();
16410
16411 // backwards compat
16412 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16413  * Based on:
16414  * Ext JS Library 1.1.1
16415  * Copyright(c) 2006-2007, Ext JS, LLC.
16416  *
16417  * Originally Released Under LGPL - original licence link has changed is not relivant.
16418  *
16419  * Fork - LGPL
16420  * <script type="text/javascript">
16421  */
16422  
16423
16424 /**
16425  * @class Roo.tree.TreePanel
16426  * @extends Roo.data.Tree
16427
16428  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16429  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16430  * @cfg {Boolean} enableDD true to enable drag and drop
16431  * @cfg {Boolean} enableDrag true to enable just drag
16432  * @cfg {Boolean} enableDrop true to enable just drop
16433  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16434  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16435  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16436  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16437  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16438  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16439  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16440  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16441  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16442  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16443  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16444  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16445  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
16446  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16447  * @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>
16448  * @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>
16449  * 
16450  * @constructor
16451  * @param {String/HTMLElement/Element} el The container element
16452  * @param {Object} config
16453  */
16454 Roo.tree.TreePanel = function(el, config){
16455     var root = false;
16456     var loader = false;
16457     if (config.root) {
16458         root = config.root;
16459         delete config.root;
16460     }
16461     if (config.loader) {
16462         loader = config.loader;
16463         delete config.loader;
16464     }
16465     
16466     Roo.apply(this, config);
16467     Roo.tree.TreePanel.superclass.constructor.call(this);
16468     this.el = Roo.get(el);
16469     this.el.addClass('x-tree');
16470     //console.log(root);
16471     if (root) {
16472         this.setRootNode( Roo.factory(root, Roo.tree));
16473     }
16474     if (loader) {
16475         this.loader = Roo.factory(loader, Roo.tree);
16476     }
16477    /**
16478     * Read-only. The id of the container element becomes this TreePanel's id.
16479     */
16480     this.id = this.el.id;
16481     this.addEvents({
16482         /**
16483         * @event beforeload
16484         * Fires before a node is loaded, return false to cancel
16485         * @param {Node} node The node being loaded
16486         */
16487         "beforeload" : true,
16488         /**
16489         * @event load
16490         * Fires when a node is loaded
16491         * @param {Node} node The node that was loaded
16492         */
16493         "load" : true,
16494         /**
16495         * @event textchange
16496         * Fires when the text for a node is changed
16497         * @param {Node} node The node
16498         * @param {String} text The new text
16499         * @param {String} oldText The old text
16500         */
16501         "textchange" : true,
16502         /**
16503         * @event beforeexpand
16504         * Fires before a node is expanded, return false to cancel.
16505         * @param {Node} node The node
16506         * @param {Boolean} deep
16507         * @param {Boolean} anim
16508         */
16509         "beforeexpand" : true,
16510         /**
16511         * @event beforecollapse
16512         * Fires before a node is collapsed, return false to cancel.
16513         * @param {Node} node The node
16514         * @param {Boolean} deep
16515         * @param {Boolean} anim
16516         */
16517         "beforecollapse" : true,
16518         /**
16519         * @event expand
16520         * Fires when a node is expanded
16521         * @param {Node} node The node
16522         */
16523         "expand" : true,
16524         /**
16525         * @event disabledchange
16526         * Fires when the disabled status of a node changes
16527         * @param {Node} node The node
16528         * @param {Boolean} disabled
16529         */
16530         "disabledchange" : true,
16531         /**
16532         * @event collapse
16533         * Fires when a node is collapsed
16534         * @param {Node} node The node
16535         */
16536         "collapse" : true,
16537         /**
16538         * @event beforeclick
16539         * Fires before click processing on a node. Return false to cancel the default action.
16540         * @param {Node} node The node
16541         * @param {Roo.EventObject} e The event object
16542         */
16543         "beforeclick":true,
16544         /**
16545         * @event checkchange
16546         * Fires when a node with a checkbox's checked property changes
16547         * @param {Node} this This node
16548         * @param {Boolean} checked
16549         */
16550         "checkchange":true,
16551         /**
16552         * @event click
16553         * Fires when a node is clicked
16554         * @param {Node} node The node
16555         * @param {Roo.EventObject} e The event object
16556         */
16557         "click":true,
16558         /**
16559         * @event dblclick
16560         * Fires when a node is double clicked
16561         * @param {Node} node The node
16562         * @param {Roo.EventObject} e The event object
16563         */
16564         "dblclick":true,
16565         /**
16566         * @event contextmenu
16567         * Fires when a node is right clicked
16568         * @param {Node} node The node
16569         * @param {Roo.EventObject} e The event object
16570         */
16571         "contextmenu":true,
16572         /**
16573         * @event beforechildrenrendered
16574         * Fires right before the child nodes for a node are rendered
16575         * @param {Node} node The node
16576         */
16577         "beforechildrenrendered":true,
16578         /**
16579         * @event startdrag
16580         * Fires when a node starts being dragged
16581         * @param {Roo.tree.TreePanel} this
16582         * @param {Roo.tree.TreeNode} node
16583         * @param {event} e The raw browser event
16584         */ 
16585        "startdrag" : true,
16586        /**
16587         * @event enddrag
16588         * Fires when a drag operation is complete
16589         * @param {Roo.tree.TreePanel} this
16590         * @param {Roo.tree.TreeNode} node
16591         * @param {event} e The raw browser event
16592         */
16593        "enddrag" : true,
16594        /**
16595         * @event dragdrop
16596         * Fires when a dragged node is dropped on a valid DD target
16597         * @param {Roo.tree.TreePanel} this
16598         * @param {Roo.tree.TreeNode} node
16599         * @param {DD} dd The dd it was dropped on
16600         * @param {event} e The raw browser event
16601         */
16602        "dragdrop" : true,
16603        /**
16604         * @event beforenodedrop
16605         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16606         * passed to handlers has the following properties:<br />
16607         * <ul style="padding:5px;padding-left:16px;">
16608         * <li>tree - The TreePanel</li>
16609         * <li>target - The node being targeted for the drop</li>
16610         * <li>data - The drag data from the drag source</li>
16611         * <li>point - The point of the drop - append, above or below</li>
16612         * <li>source - The drag source</li>
16613         * <li>rawEvent - Raw mouse event</li>
16614         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16615         * to be inserted by setting them on this object.</li>
16616         * <li>cancel - Set this to true to cancel the drop.</li>
16617         * </ul>
16618         * @param {Object} dropEvent
16619         */
16620        "beforenodedrop" : true,
16621        /**
16622         * @event nodedrop
16623         * Fires after a DD object is dropped on a node in this tree. The dropEvent
16624         * passed to handlers has the following properties:<br />
16625         * <ul style="padding:5px;padding-left:16px;">
16626         * <li>tree - The TreePanel</li>
16627         * <li>target - The node being targeted for the drop</li>
16628         * <li>data - The drag data from the drag source</li>
16629         * <li>point - The point of the drop - append, above or below</li>
16630         * <li>source - The drag source</li>
16631         * <li>rawEvent - Raw mouse event</li>
16632         * <li>dropNode - Dropped node(s).</li>
16633         * </ul>
16634         * @param {Object} dropEvent
16635         */
16636        "nodedrop" : true,
16637         /**
16638         * @event nodedragover
16639         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
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 - Drop node(s) provided by the source.</li>
16649         * <li>cancel - Set this to true to signal drop not allowed.</li>
16650         * </ul>
16651         * @param {Object} dragOverEvent
16652         */
16653        "nodedragover" : true
16654         
16655     });
16656     if(this.singleExpand){
16657        this.on("beforeexpand", this.restrictExpand, this);
16658     }
16659     if (this.editor) {
16660         this.editor.tree = this;
16661         this.editor = Roo.factory(this.editor, Roo.tree);
16662     }
16663     
16664     if (this.selModel) {
16665         this.selModel = Roo.factory(this.selModel, Roo.tree);
16666     }
16667    
16668 };
16669 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16670     rootVisible : true,
16671     animate: Roo.enableFx,
16672     lines : true,
16673     enableDD : false,
16674     hlDrop : Roo.enableFx,
16675   
16676     renderer: false,
16677     
16678     rendererTip: false,
16679     // private
16680     restrictExpand : function(node){
16681         var p = node.parentNode;
16682         if(p){
16683             if(p.expandedChild && p.expandedChild.parentNode == p){
16684                 p.expandedChild.collapse();
16685             }
16686             p.expandedChild = node;
16687         }
16688     },
16689
16690     // private override
16691     setRootNode : function(node){
16692         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16693         if(!this.rootVisible){
16694             node.ui = new Roo.tree.RootTreeNodeUI(node);
16695         }
16696         return node;
16697     },
16698
16699     /**
16700      * Returns the container element for this TreePanel
16701      */
16702     getEl : function(){
16703         return this.el;
16704     },
16705
16706     /**
16707      * Returns the default TreeLoader for this TreePanel
16708      */
16709     getLoader : function(){
16710         return this.loader;
16711     },
16712
16713     /**
16714      * Expand all nodes
16715      */
16716     expandAll : function(){
16717         this.root.expand(true);
16718     },
16719
16720     /**
16721      * Collapse all nodes
16722      */
16723     collapseAll : function(){
16724         this.root.collapse(true);
16725     },
16726
16727     /**
16728      * Returns the selection model used by this TreePanel
16729      */
16730     getSelectionModel : function(){
16731         if(!this.selModel){
16732             this.selModel = new Roo.tree.DefaultSelectionModel();
16733         }
16734         return this.selModel;
16735     },
16736
16737     /**
16738      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16739      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16740      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16741      * @return {Array}
16742      */
16743     getChecked : function(a, startNode){
16744         startNode = startNode || this.root;
16745         var r = [];
16746         var f = function(){
16747             if(this.attributes.checked){
16748                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16749             }
16750         }
16751         startNode.cascade(f);
16752         return r;
16753     },
16754
16755     /**
16756      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16757      * @param {String} path
16758      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16759      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16760      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16761      */
16762     expandPath : function(path, attr, callback){
16763         attr = attr || "id";
16764         var keys = path.split(this.pathSeparator);
16765         var curNode = this.root;
16766         if(curNode.attributes[attr] != keys[1]){ // invalid root
16767             if(callback){
16768                 callback(false, null);
16769             }
16770             return;
16771         }
16772         var index = 1;
16773         var f = function(){
16774             if(++index == keys.length){
16775                 if(callback){
16776                     callback(true, curNode);
16777                 }
16778                 return;
16779             }
16780             var c = curNode.findChild(attr, keys[index]);
16781             if(!c){
16782                 if(callback){
16783                     callback(false, curNode);
16784                 }
16785                 return;
16786             }
16787             curNode = c;
16788             c.expand(false, false, f);
16789         };
16790         curNode.expand(false, false, f);
16791     },
16792
16793     /**
16794      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16795      * @param {String} path
16796      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16797      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16798      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16799      */
16800     selectPath : function(path, attr, callback){
16801         attr = attr || "id";
16802         var keys = path.split(this.pathSeparator);
16803         var v = keys.pop();
16804         if(keys.length > 0){
16805             var f = function(success, node){
16806                 if(success && node){
16807                     var n = node.findChild(attr, v);
16808                     if(n){
16809                         n.select();
16810                         if(callback){
16811                             callback(true, n);
16812                         }
16813                     }else if(callback){
16814                         callback(false, n);
16815                     }
16816                 }else{
16817                     if(callback){
16818                         callback(false, n);
16819                     }
16820                 }
16821             };
16822             this.expandPath(keys.join(this.pathSeparator), attr, f);
16823         }else{
16824             this.root.select();
16825             if(callback){
16826                 callback(true, this.root);
16827             }
16828         }
16829     },
16830
16831     getTreeEl : function(){
16832         return this.el;
16833     },
16834
16835     /**
16836      * Trigger rendering of this TreePanel
16837      */
16838     render : function(){
16839         if (this.innerCt) {
16840             return this; // stop it rendering more than once!!
16841         }
16842         
16843         this.innerCt = this.el.createChild({tag:"ul",
16844                cls:"x-tree-root-ct " +
16845                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16846
16847         if(this.containerScroll){
16848             Roo.dd.ScrollManager.register(this.el);
16849         }
16850         if((this.enableDD || this.enableDrop) && !this.dropZone){
16851            /**
16852             * The dropZone used by this tree if drop is enabled
16853             * @type Roo.tree.TreeDropZone
16854             */
16855              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16856                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16857            });
16858         }
16859         if((this.enableDD || this.enableDrag) && !this.dragZone){
16860            /**
16861             * The dragZone used by this tree if drag is enabled
16862             * @type Roo.tree.TreeDragZone
16863             */
16864             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16865                ddGroup: this.ddGroup || "TreeDD",
16866                scroll: this.ddScroll
16867            });
16868         }
16869         this.getSelectionModel().init(this);
16870         if (!this.root) {
16871             console.log("ROOT not set in tree");
16872             return;
16873         }
16874         this.root.render();
16875         if(!this.rootVisible){
16876             this.root.renderChildren();
16877         }
16878         return this;
16879     }
16880 });/*
16881  * Based on:
16882  * Ext JS Library 1.1.1
16883  * Copyright(c) 2006-2007, Ext JS, LLC.
16884  *
16885  * Originally Released Under LGPL - original licence link has changed is not relivant.
16886  *
16887  * Fork - LGPL
16888  * <script type="text/javascript">
16889  */
16890  
16891
16892 /**
16893  * @class Roo.tree.DefaultSelectionModel
16894  * @extends Roo.util.Observable
16895  * The default single selection for a TreePanel.
16896  * @param {Object} cfg Configuration
16897  */
16898 Roo.tree.DefaultSelectionModel = function(cfg){
16899    this.selNode = null;
16900    
16901    
16902    
16903    this.addEvents({
16904        /**
16905         * @event selectionchange
16906         * Fires when the selected node changes
16907         * @param {DefaultSelectionModel} this
16908         * @param {TreeNode} node the new selection
16909         */
16910        "selectionchange" : true,
16911
16912        /**
16913         * @event beforeselect
16914         * Fires before the selected node changes, return false to cancel the change
16915         * @param {DefaultSelectionModel} this
16916         * @param {TreeNode} node the new selection
16917         * @param {TreeNode} node the old selection
16918         */
16919        "beforeselect" : true
16920    });
16921    
16922     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
16923 };
16924
16925 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16926     init : function(tree){
16927         this.tree = tree;
16928         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16929         tree.on("click", this.onNodeClick, this);
16930     },
16931     
16932     onNodeClick : function(node, e){
16933         if (e.ctrlKey && this.selNode == node)  {
16934             this.unselect(node);
16935             return;
16936         }
16937         this.select(node);
16938     },
16939     
16940     /**
16941      * Select a node.
16942      * @param {TreeNode} node The node to select
16943      * @return {TreeNode} The selected node
16944      */
16945     select : function(node){
16946         var last = this.selNode;
16947         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16948             if(last){
16949                 last.ui.onSelectedChange(false);
16950             }
16951             this.selNode = node;
16952             node.ui.onSelectedChange(true);
16953             this.fireEvent("selectionchange", this, node, last);
16954         }
16955         return node;
16956     },
16957     
16958     /**
16959      * Deselect a node.
16960      * @param {TreeNode} node The node to unselect
16961      */
16962     unselect : function(node){
16963         if(this.selNode == node){
16964             this.clearSelections();
16965         }    
16966     },
16967     
16968     /**
16969      * Clear all selections
16970      */
16971     clearSelections : function(){
16972         var n = this.selNode;
16973         if(n){
16974             n.ui.onSelectedChange(false);
16975             this.selNode = null;
16976             this.fireEvent("selectionchange", this, null);
16977         }
16978         return n;
16979     },
16980     
16981     /**
16982      * Get the selected node
16983      * @return {TreeNode} The selected node
16984      */
16985     getSelectedNode : function(){
16986         return this.selNode;    
16987     },
16988     
16989     /**
16990      * Returns true if the node is selected
16991      * @param {TreeNode} node The node to check
16992      * @return {Boolean}
16993      */
16994     isSelected : function(node){
16995         return this.selNode == node;  
16996     },
16997
16998     /**
16999      * Selects the node above the selected node in the tree, intelligently walking the nodes
17000      * @return TreeNode The new selection
17001      */
17002     selectPrevious : function(){
17003         var s = this.selNode || this.lastSelNode;
17004         if(!s){
17005             return null;
17006         }
17007         var ps = s.previousSibling;
17008         if(ps){
17009             if(!ps.isExpanded() || ps.childNodes.length < 1){
17010                 return this.select(ps);
17011             } else{
17012                 var lc = ps.lastChild;
17013                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
17014                     lc = lc.lastChild;
17015                 }
17016                 return this.select(lc);
17017             }
17018         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
17019             return this.select(s.parentNode);
17020         }
17021         return null;
17022     },
17023
17024     /**
17025      * Selects the node above the selected node in the tree, intelligently walking the nodes
17026      * @return TreeNode The new selection
17027      */
17028     selectNext : function(){
17029         var s = this.selNode || this.lastSelNode;
17030         if(!s){
17031             return null;
17032         }
17033         if(s.firstChild && s.isExpanded()){
17034              return this.select(s.firstChild);
17035          }else if(s.nextSibling){
17036              return this.select(s.nextSibling);
17037          }else if(s.parentNode){
17038             var newS = null;
17039             s.parentNode.bubble(function(){
17040                 if(this.nextSibling){
17041                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
17042                     return false;
17043                 }
17044             });
17045             return newS;
17046          }
17047         return null;
17048     },
17049
17050     onKeyDown : function(e){
17051         var s = this.selNode || this.lastSelNode;
17052         // undesirable, but required
17053         var sm = this;
17054         if(!s){
17055             return;
17056         }
17057         var k = e.getKey();
17058         switch(k){
17059              case e.DOWN:
17060                  e.stopEvent();
17061                  this.selectNext();
17062              break;
17063              case e.UP:
17064                  e.stopEvent();
17065                  this.selectPrevious();
17066              break;
17067              case e.RIGHT:
17068                  e.preventDefault();
17069                  if(s.hasChildNodes()){
17070                      if(!s.isExpanded()){
17071                          s.expand();
17072                      }else if(s.firstChild){
17073                          this.select(s.firstChild, e);
17074                      }
17075                  }
17076              break;
17077              case e.LEFT:
17078                  e.preventDefault();
17079                  if(s.hasChildNodes() && s.isExpanded()){
17080                      s.collapse();
17081                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
17082                      this.select(s.parentNode, e);
17083                  }
17084              break;
17085         };
17086     }
17087 });
17088
17089 /**
17090  * @class Roo.tree.MultiSelectionModel
17091  * @extends Roo.util.Observable
17092  * Multi selection for a TreePanel.
17093  * @param {Object} cfg Configuration
17094  */
17095 Roo.tree.MultiSelectionModel = function(){
17096    this.selNodes = [];
17097    this.selMap = {};
17098    this.addEvents({
17099        /**
17100         * @event selectionchange
17101         * Fires when the selected nodes change
17102         * @param {MultiSelectionModel} this
17103         * @param {Array} nodes Array of the selected nodes
17104         */
17105        "selectionchange" : true
17106    });
17107    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
17108    
17109 };
17110
17111 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
17112     init : function(tree){
17113         this.tree = tree;
17114         tree.getTreeEl().on("keydown", this.onKeyDown, this);
17115         tree.on("click", this.onNodeClick, this);
17116     },
17117     
17118     onNodeClick : function(node, e){
17119         this.select(node, e, e.ctrlKey);
17120     },
17121     
17122     /**
17123      * Select a node.
17124      * @param {TreeNode} node The node to select
17125      * @param {EventObject} e (optional) An event associated with the selection
17126      * @param {Boolean} keepExisting True to retain existing selections
17127      * @return {TreeNode} The selected node
17128      */
17129     select : function(node, e, keepExisting){
17130         if(keepExisting !== true){
17131             this.clearSelections(true);
17132         }
17133         if(this.isSelected(node)){
17134             this.lastSelNode = node;
17135             return node;
17136         }
17137         this.selNodes.push(node);
17138         this.selMap[node.id] = node;
17139         this.lastSelNode = node;
17140         node.ui.onSelectedChange(true);
17141         this.fireEvent("selectionchange", this, this.selNodes);
17142         return node;
17143     },
17144     
17145     /**
17146      * Deselect a node.
17147      * @param {TreeNode} node The node to unselect
17148      */
17149     unselect : function(node){
17150         if(this.selMap[node.id]){
17151             node.ui.onSelectedChange(false);
17152             var sn = this.selNodes;
17153             var index = -1;
17154             if(sn.indexOf){
17155                 index = sn.indexOf(node);
17156             }else{
17157                 for(var i = 0, len = sn.length; i < len; i++){
17158                     if(sn[i] == node){
17159                         index = i;
17160                         break;
17161                     }
17162                 }
17163             }
17164             if(index != -1){
17165                 this.selNodes.splice(index, 1);
17166             }
17167             delete this.selMap[node.id];
17168             this.fireEvent("selectionchange", this, this.selNodes);
17169         }
17170     },
17171     
17172     /**
17173      * Clear all selections
17174      */
17175     clearSelections : function(suppressEvent){
17176         var sn = this.selNodes;
17177         if(sn.length > 0){
17178             for(var i = 0, len = sn.length; i < len; i++){
17179                 sn[i].ui.onSelectedChange(false);
17180             }
17181             this.selNodes = [];
17182             this.selMap = {};
17183             if(suppressEvent !== true){
17184                 this.fireEvent("selectionchange", this, this.selNodes);
17185             }
17186         }
17187     },
17188     
17189     /**
17190      * Returns true if the node is selected
17191      * @param {TreeNode} node The node to check
17192      * @return {Boolean}
17193      */
17194     isSelected : function(node){
17195         return this.selMap[node.id] ? true : false;  
17196     },
17197     
17198     /**
17199      * Returns an array of the selected nodes
17200      * @return {Array}
17201      */
17202     getSelectedNodes : function(){
17203         return this.selNodes;    
17204     },
17205
17206     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17207
17208     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17209
17210     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17211 });/*
17212  * Based on:
17213  * Ext JS Library 1.1.1
17214  * Copyright(c) 2006-2007, Ext JS, LLC.
17215  *
17216  * Originally Released Under LGPL - original licence link has changed is not relivant.
17217  *
17218  * Fork - LGPL
17219  * <script type="text/javascript">
17220  */
17221  
17222 /**
17223  * @class Roo.tree.TreeNode
17224  * @extends Roo.data.Node
17225  * @cfg {String} text The text for this node
17226  * @cfg {Boolean} expanded true to start the node expanded
17227  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17228  * @cfg {Boolean} allowDrop false if this node cannot be drop on
17229  * @cfg {Boolean} disabled true to start the node disabled
17230  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17231  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17232  * @cfg {String} cls A css class to be added to the node
17233  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17234  * @cfg {String} href URL of the link used for the node (defaults to #)
17235  * @cfg {String} hrefTarget target frame for the link
17236  * @cfg {String} qtip An Ext QuickTip for the node
17237  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17238  * @cfg {Boolean} singleClickExpand True for single click expand on this node
17239  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17240  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17241  * (defaults to undefined with no checkbox rendered)
17242  * @constructor
17243  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17244  */
17245 Roo.tree.TreeNode = function(attributes){
17246     attributes = attributes || {};
17247     if(typeof attributes == "string"){
17248         attributes = {text: attributes};
17249     }
17250     this.childrenRendered = false;
17251     this.rendered = false;
17252     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17253     this.expanded = attributes.expanded === true;
17254     this.isTarget = attributes.isTarget !== false;
17255     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17256     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17257
17258     /**
17259      * Read-only. The text for this node. To change it use setText().
17260      * @type String
17261      */
17262     this.text = attributes.text;
17263     /**
17264      * True if this node is disabled.
17265      * @type Boolean
17266      */
17267     this.disabled = attributes.disabled === true;
17268
17269     this.addEvents({
17270         /**
17271         * @event textchange
17272         * Fires when the text for this node is changed
17273         * @param {Node} this This node
17274         * @param {String} text The new text
17275         * @param {String} oldText The old text
17276         */
17277         "textchange" : true,
17278         /**
17279         * @event beforeexpand
17280         * Fires before this node is expanded, return false to cancel.
17281         * @param {Node} this This node
17282         * @param {Boolean} deep
17283         * @param {Boolean} anim
17284         */
17285         "beforeexpand" : true,
17286         /**
17287         * @event beforecollapse
17288         * Fires before this node is collapsed, return false to cancel.
17289         * @param {Node} this This node
17290         * @param {Boolean} deep
17291         * @param {Boolean} anim
17292         */
17293         "beforecollapse" : true,
17294         /**
17295         * @event expand
17296         * Fires when this node is expanded
17297         * @param {Node} this This node
17298         */
17299         "expand" : true,
17300         /**
17301         * @event disabledchange
17302         * Fires when the disabled status of this node changes
17303         * @param {Node} this This node
17304         * @param {Boolean} disabled
17305         */
17306         "disabledchange" : true,
17307         /**
17308         * @event collapse
17309         * Fires when this node is collapsed
17310         * @param {Node} this This node
17311         */
17312         "collapse" : true,
17313         /**
17314         * @event beforeclick
17315         * Fires before click processing. Return false to cancel the default action.
17316         * @param {Node} this This node
17317         * @param {Roo.EventObject} e The event object
17318         */
17319         "beforeclick":true,
17320         /**
17321         * @event checkchange
17322         * Fires when a node with a checkbox's checked property changes
17323         * @param {Node} this This node
17324         * @param {Boolean} checked
17325         */
17326         "checkchange":true,
17327         /**
17328         * @event click
17329         * Fires when this node is clicked
17330         * @param {Node} this This node
17331         * @param {Roo.EventObject} e The event object
17332         */
17333         "click":true,
17334         /**
17335         * @event dblclick
17336         * Fires when this node is double clicked
17337         * @param {Node} this This node
17338         * @param {Roo.EventObject} e The event object
17339         */
17340         "dblclick":true,
17341         /**
17342         * @event contextmenu
17343         * Fires when this node is right clicked
17344         * @param {Node} this This node
17345         * @param {Roo.EventObject} e The event object
17346         */
17347         "contextmenu":true,
17348         /**
17349         * @event beforechildrenrendered
17350         * Fires right before the child nodes for this node are rendered
17351         * @param {Node} this This node
17352         */
17353         "beforechildrenrendered":true
17354     });
17355
17356     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17357
17358     /**
17359      * Read-only. The UI for this node
17360      * @type TreeNodeUI
17361      */
17362     this.ui = new uiClass(this);
17363 };
17364 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17365     preventHScroll: true,
17366     /**
17367      * Returns true if this node is expanded
17368      * @return {Boolean}
17369      */
17370     isExpanded : function(){
17371         return this.expanded;
17372     },
17373
17374     /**
17375      * Returns the UI object for this node
17376      * @return {TreeNodeUI}
17377      */
17378     getUI : function(){
17379         return this.ui;
17380     },
17381
17382     // private override
17383     setFirstChild : function(node){
17384         var of = this.firstChild;
17385         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17386         if(this.childrenRendered && of && node != of){
17387             of.renderIndent(true, true);
17388         }
17389         if(this.rendered){
17390             this.renderIndent(true, true);
17391         }
17392     },
17393
17394     // private override
17395     setLastChild : function(node){
17396         var ol = this.lastChild;
17397         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17398         if(this.childrenRendered && ol && node != ol){
17399             ol.renderIndent(true, true);
17400         }
17401         if(this.rendered){
17402             this.renderIndent(true, true);
17403         }
17404     },
17405
17406     // these methods are overridden to provide lazy rendering support
17407     // private override
17408     appendChild : function(){
17409         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17410         if(node && this.childrenRendered){
17411             node.render();
17412         }
17413         this.ui.updateExpandIcon();
17414         return node;
17415     },
17416
17417     // private override
17418     removeChild : function(node){
17419         this.ownerTree.getSelectionModel().unselect(node);
17420         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17421         // if it's been rendered remove dom node
17422         if(this.childrenRendered){
17423             node.ui.remove();
17424         }
17425         if(this.childNodes.length < 1){
17426             this.collapse(false, false);
17427         }else{
17428             this.ui.updateExpandIcon();
17429         }
17430         if(!this.firstChild) {
17431             this.childrenRendered = false;
17432         }
17433         return node;
17434     },
17435
17436     // private override
17437     insertBefore : function(node, refNode){
17438         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17439         if(newNode && refNode && this.childrenRendered){
17440             node.render();
17441         }
17442         this.ui.updateExpandIcon();
17443         return newNode;
17444     },
17445
17446     /**
17447      * Sets the text for this node
17448      * @param {String} text
17449      */
17450     setText : function(text){
17451         var oldText = this.text;
17452         this.text = text;
17453         this.attributes.text = text;
17454         if(this.rendered){ // event without subscribing
17455             this.ui.onTextChange(this, text, oldText);
17456         }
17457         this.fireEvent("textchange", this, text, oldText);
17458     },
17459
17460     /**
17461      * Triggers selection of this node
17462      */
17463     select : function(){
17464         this.getOwnerTree().getSelectionModel().select(this);
17465     },
17466
17467     /**
17468      * Triggers deselection of this node
17469      */
17470     unselect : function(){
17471         this.getOwnerTree().getSelectionModel().unselect(this);
17472     },
17473
17474     /**
17475      * Returns true if this node is selected
17476      * @return {Boolean}
17477      */
17478     isSelected : function(){
17479         return this.getOwnerTree().getSelectionModel().isSelected(this);
17480     },
17481
17482     /**
17483      * Expand this node.
17484      * @param {Boolean} deep (optional) True to expand all children as well
17485      * @param {Boolean} anim (optional) false to cancel the default animation
17486      * @param {Function} callback (optional) A callback to be called when
17487      * expanding this node completes (does not wait for deep expand to complete).
17488      * Called with 1 parameter, this node.
17489      */
17490     expand : function(deep, anim, callback){
17491         if(!this.expanded){
17492             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17493                 return;
17494             }
17495             if(!this.childrenRendered){
17496                 this.renderChildren();
17497             }
17498             this.expanded = true;
17499             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17500                 this.ui.animExpand(function(){
17501                     this.fireEvent("expand", this);
17502                     if(typeof callback == "function"){
17503                         callback(this);
17504                     }
17505                     if(deep === true){
17506                         this.expandChildNodes(true);
17507                     }
17508                 }.createDelegate(this));
17509                 return;
17510             }else{
17511                 this.ui.expand();
17512                 this.fireEvent("expand", this);
17513                 if(typeof callback == "function"){
17514                     callback(this);
17515                 }
17516             }
17517         }else{
17518            if(typeof callback == "function"){
17519                callback(this);
17520            }
17521         }
17522         if(deep === true){
17523             this.expandChildNodes(true);
17524         }
17525     },
17526
17527     isHiddenRoot : function(){
17528         return this.isRoot && !this.getOwnerTree().rootVisible;
17529     },
17530
17531     /**
17532      * Collapse this node.
17533      * @param {Boolean} deep (optional) True to collapse all children as well
17534      * @param {Boolean} anim (optional) false to cancel the default animation
17535      */
17536     collapse : function(deep, anim){
17537         if(this.expanded && !this.isHiddenRoot()){
17538             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17539                 return;
17540             }
17541             this.expanded = false;
17542             if((this.getOwnerTree().animate && anim !== false) || anim){
17543                 this.ui.animCollapse(function(){
17544                     this.fireEvent("collapse", this);
17545                     if(deep === true){
17546                         this.collapseChildNodes(true);
17547                     }
17548                 }.createDelegate(this));
17549                 return;
17550             }else{
17551                 this.ui.collapse();
17552                 this.fireEvent("collapse", this);
17553             }
17554         }
17555         if(deep === true){
17556             var cs = this.childNodes;
17557             for(var i = 0, len = cs.length; i < len; i++) {
17558                 cs[i].collapse(true, false);
17559             }
17560         }
17561     },
17562
17563     // private
17564     delayedExpand : function(delay){
17565         if(!this.expandProcId){
17566             this.expandProcId = this.expand.defer(delay, this);
17567         }
17568     },
17569
17570     // private
17571     cancelExpand : function(){
17572         if(this.expandProcId){
17573             clearTimeout(this.expandProcId);
17574         }
17575         this.expandProcId = false;
17576     },
17577
17578     /**
17579      * Toggles expanded/collapsed state of the node
17580      */
17581     toggle : function(){
17582         if(this.expanded){
17583             this.collapse();
17584         }else{
17585             this.expand();
17586         }
17587     },
17588
17589     /**
17590      * Ensures all parent nodes are expanded
17591      */
17592     ensureVisible : function(callback){
17593         var tree = this.getOwnerTree();
17594         tree.expandPath(this.parentNode.getPath(), false, function(){
17595             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17596             Roo.callback(callback);
17597         }.createDelegate(this));
17598     },
17599
17600     /**
17601      * Expand all child nodes
17602      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17603      */
17604     expandChildNodes : function(deep){
17605         var cs = this.childNodes;
17606         for(var i = 0, len = cs.length; i < len; i++) {
17607                 cs[i].expand(deep);
17608         }
17609     },
17610
17611     /**
17612      * Collapse all child nodes
17613      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17614      */
17615     collapseChildNodes : function(deep){
17616         var cs = this.childNodes;
17617         for(var i = 0, len = cs.length; i < len; i++) {
17618                 cs[i].collapse(deep);
17619         }
17620     },
17621
17622     /**
17623      * Disables this node
17624      */
17625     disable : function(){
17626         this.disabled = true;
17627         this.unselect();
17628         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17629             this.ui.onDisableChange(this, true);
17630         }
17631         this.fireEvent("disabledchange", this, true);
17632     },
17633
17634     /**
17635      * Enables this node
17636      */
17637     enable : function(){
17638         this.disabled = false;
17639         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17640             this.ui.onDisableChange(this, false);
17641         }
17642         this.fireEvent("disabledchange", this, false);
17643     },
17644
17645     // private
17646     renderChildren : function(suppressEvent){
17647         if(suppressEvent !== false){
17648             this.fireEvent("beforechildrenrendered", this);
17649         }
17650         var cs = this.childNodes;
17651         for(var i = 0, len = cs.length; i < len; i++){
17652             cs[i].render(true);
17653         }
17654         this.childrenRendered = true;
17655     },
17656
17657     // private
17658     sort : function(fn, scope){
17659         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17660         if(this.childrenRendered){
17661             var cs = this.childNodes;
17662             for(var i = 0, len = cs.length; i < len; i++){
17663                 cs[i].render(true);
17664             }
17665         }
17666     },
17667
17668     // private
17669     render : function(bulkRender){
17670         this.ui.render(bulkRender);
17671         if(!this.rendered){
17672             this.rendered = true;
17673             if(this.expanded){
17674                 this.expanded = false;
17675                 this.expand(false, false);
17676             }
17677         }
17678     },
17679
17680     // private
17681     renderIndent : function(deep, refresh){
17682         if(refresh){
17683             this.ui.childIndent = null;
17684         }
17685         this.ui.renderIndent();
17686         if(deep === true && this.childrenRendered){
17687             var cs = this.childNodes;
17688             for(var i = 0, len = cs.length; i < len; i++){
17689                 cs[i].renderIndent(true, refresh);
17690             }
17691         }
17692     }
17693 });/*
17694  * Based on:
17695  * Ext JS Library 1.1.1
17696  * Copyright(c) 2006-2007, Ext JS, LLC.
17697  *
17698  * Originally Released Under LGPL - original licence link has changed is not relivant.
17699  *
17700  * Fork - LGPL
17701  * <script type="text/javascript">
17702  */
17703  
17704 /**
17705  * @class Roo.tree.AsyncTreeNode
17706  * @extends Roo.tree.TreeNode
17707  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17708  * @constructor
17709  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17710  */
17711  Roo.tree.AsyncTreeNode = function(config){
17712     this.loaded = false;
17713     this.loading = false;
17714     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17715     /**
17716     * @event beforeload
17717     * Fires before this node is loaded, return false to cancel
17718     * @param {Node} this This node
17719     */
17720     this.addEvents({'beforeload':true, 'load': true});
17721     /**
17722     * @event load
17723     * Fires when this node is loaded
17724     * @param {Node} this This node
17725     */
17726     /**
17727      * The loader used by this node (defaults to using the tree's defined loader)
17728      * @type TreeLoader
17729      * @property loader
17730      */
17731 };
17732 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17733     expand : function(deep, anim, callback){
17734         if(this.loading){ // if an async load is already running, waiting til it's done
17735             var timer;
17736             var f = function(){
17737                 if(!this.loading){ // done loading
17738                     clearInterval(timer);
17739                     this.expand(deep, anim, callback);
17740                 }
17741             }.createDelegate(this);
17742             timer = setInterval(f, 200);
17743             return;
17744         }
17745         if(!this.loaded){
17746             if(this.fireEvent("beforeload", this) === false){
17747                 return;
17748             }
17749             this.loading = true;
17750             this.ui.beforeLoad(this);
17751             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17752             if(loader){
17753                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17754                 return;
17755             }
17756         }
17757         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17758     },
17759     
17760     /**
17761      * Returns true if this node is currently loading
17762      * @return {Boolean}
17763      */
17764     isLoading : function(){
17765         return this.loading;  
17766     },
17767     
17768     loadComplete : function(deep, anim, callback){
17769         this.loading = false;
17770         this.loaded = true;
17771         this.ui.afterLoad(this);
17772         this.fireEvent("load", this);
17773         this.expand(deep, anim, callback);
17774     },
17775     
17776     /**
17777      * Returns true if this node has been loaded
17778      * @return {Boolean}
17779      */
17780     isLoaded : function(){
17781         return this.loaded;
17782     },
17783     
17784     hasChildNodes : function(){
17785         if(!this.isLeaf() && !this.loaded){
17786             return true;
17787         }else{
17788             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17789         }
17790     },
17791
17792     /**
17793      * Trigger a reload for this node
17794      * @param {Function} callback
17795      */
17796     reload : function(callback){
17797         this.collapse(false, false);
17798         while(this.firstChild){
17799             this.removeChild(this.firstChild);
17800         }
17801         this.childrenRendered = false;
17802         this.loaded = false;
17803         if(this.isHiddenRoot()){
17804             this.expanded = false;
17805         }
17806         this.expand(false, false, callback);
17807     }
17808 });/*
17809  * Based on:
17810  * Ext JS Library 1.1.1
17811  * Copyright(c) 2006-2007, Ext JS, LLC.
17812  *
17813  * Originally Released Under LGPL - original licence link has changed is not relivant.
17814  *
17815  * Fork - LGPL
17816  * <script type="text/javascript">
17817  */
17818  
17819 /**
17820  * @class Roo.tree.TreeNodeUI
17821  * @constructor
17822  * @param {Object} node The node to render
17823  * The TreeNode UI implementation is separate from the
17824  * tree implementation. Unless you are customizing the tree UI,
17825  * you should never have to use this directly.
17826  */
17827 Roo.tree.TreeNodeUI = function(node){
17828     this.node = node;
17829     this.rendered = false;
17830     this.animating = false;
17831     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17832 };
17833
17834 Roo.tree.TreeNodeUI.prototype = {
17835     removeChild : function(node){
17836         if(this.rendered){
17837             this.ctNode.removeChild(node.ui.getEl());
17838         }
17839     },
17840
17841     beforeLoad : function(){
17842          this.addClass("x-tree-node-loading");
17843     },
17844
17845     afterLoad : function(){
17846          this.removeClass("x-tree-node-loading");
17847     },
17848
17849     onTextChange : function(node, text, oldText){
17850         if(this.rendered){
17851             this.textNode.innerHTML = text;
17852         }
17853     },
17854
17855     onDisableChange : function(node, state){
17856         this.disabled = state;
17857         if(state){
17858             this.addClass("x-tree-node-disabled");
17859         }else{
17860             this.removeClass("x-tree-node-disabled");
17861         }
17862     },
17863
17864     onSelectedChange : function(state){
17865         if(state){
17866             this.focus();
17867             this.addClass("x-tree-selected");
17868         }else{
17869             //this.blur();
17870             this.removeClass("x-tree-selected");
17871         }
17872     },
17873
17874     onMove : function(tree, node, oldParent, newParent, index, refNode){
17875         this.childIndent = null;
17876         if(this.rendered){
17877             var targetNode = newParent.ui.getContainer();
17878             if(!targetNode){//target not rendered
17879                 this.holder = document.createElement("div");
17880                 this.holder.appendChild(this.wrap);
17881                 return;
17882             }
17883             var insertBefore = refNode ? refNode.ui.getEl() : null;
17884             if(insertBefore){
17885                 targetNode.insertBefore(this.wrap, insertBefore);
17886             }else{
17887                 targetNode.appendChild(this.wrap);
17888             }
17889             this.node.renderIndent(true);
17890         }
17891     },
17892
17893     addClass : function(cls){
17894         if(this.elNode){
17895             Roo.fly(this.elNode).addClass(cls);
17896         }
17897     },
17898
17899     removeClass : function(cls){
17900         if(this.elNode){
17901             Roo.fly(this.elNode).removeClass(cls);
17902         }
17903     },
17904
17905     remove : function(){
17906         if(this.rendered){
17907             this.holder = document.createElement("div");
17908             this.holder.appendChild(this.wrap);
17909         }
17910     },
17911
17912     fireEvent : function(){
17913         return this.node.fireEvent.apply(this.node, arguments);
17914     },
17915
17916     initEvents : function(){
17917         this.node.on("move", this.onMove, this);
17918         var E = Roo.EventManager;
17919         var a = this.anchor;
17920
17921         var el = Roo.fly(a, '_treeui');
17922
17923         if(Roo.isOpera){ // opera render bug ignores the CSS
17924             el.setStyle("text-decoration", "none");
17925         }
17926
17927         el.on("click", this.onClick, this);
17928         el.on("dblclick", this.onDblClick, this);
17929
17930         if(this.checkbox){
17931             Roo.EventManager.on(this.checkbox,
17932                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17933         }
17934
17935         el.on("contextmenu", this.onContextMenu, this);
17936
17937         var icon = Roo.fly(this.iconNode);
17938         icon.on("click", this.onClick, this);
17939         icon.on("dblclick", this.onDblClick, this);
17940         icon.on("contextmenu", this.onContextMenu, this);
17941         E.on(this.ecNode, "click", this.ecClick, this, true);
17942
17943         if(this.node.disabled){
17944             this.addClass("x-tree-node-disabled");
17945         }
17946         if(this.node.hidden){
17947             this.addClass("x-tree-node-disabled");
17948         }
17949         var ot = this.node.getOwnerTree();
17950         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17951         if(dd && (!this.node.isRoot || ot.rootVisible)){
17952             Roo.dd.Registry.register(this.elNode, {
17953                 node: this.node,
17954                 handles: this.getDDHandles(),
17955                 isHandle: false
17956             });
17957         }
17958     },
17959
17960     getDDHandles : function(){
17961         return [this.iconNode, this.textNode];
17962     },
17963
17964     hide : function(){
17965         if(this.rendered){
17966             this.wrap.style.display = "none";
17967         }
17968     },
17969
17970     show : function(){
17971         if(this.rendered){
17972             this.wrap.style.display = "";
17973         }
17974     },
17975
17976     onContextMenu : function(e){
17977         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
17978             e.preventDefault();
17979             this.focus();
17980             this.fireEvent("contextmenu", this.node, e);
17981         }
17982     },
17983
17984     onClick : function(e){
17985         if(this.dropping){
17986             e.stopEvent();
17987             return;
17988         }
17989         if(this.fireEvent("beforeclick", this.node, e) !== false){
17990             if(!this.disabled && this.node.attributes.href){
17991                 this.fireEvent("click", this.node, e);
17992                 return;
17993             }
17994             e.preventDefault();
17995             if(this.disabled){
17996                 return;
17997             }
17998
17999             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
18000                 this.node.toggle();
18001             }
18002
18003             this.fireEvent("click", this.node, e);
18004         }else{
18005             e.stopEvent();
18006         }
18007     },
18008
18009     onDblClick : function(e){
18010         e.preventDefault();
18011         if(this.disabled){
18012             return;
18013         }
18014         if(this.checkbox){
18015             this.toggleCheck();
18016         }
18017         if(!this.animating && this.node.hasChildNodes()){
18018             this.node.toggle();
18019         }
18020         this.fireEvent("dblclick", this.node, e);
18021     },
18022
18023     onCheckChange : function(){
18024         var checked = this.checkbox.checked;
18025         this.node.attributes.checked = checked;
18026         this.fireEvent('checkchange', this.node, checked);
18027     },
18028
18029     ecClick : function(e){
18030         if(!this.animating && this.node.hasChildNodes()){
18031             this.node.toggle();
18032         }
18033     },
18034
18035     startDrop : function(){
18036         this.dropping = true;
18037     },
18038
18039     // delayed drop so the click event doesn't get fired on a drop
18040     endDrop : function(){
18041        setTimeout(function(){
18042            this.dropping = false;
18043        }.createDelegate(this), 50);
18044     },
18045
18046     expand : function(){
18047         this.updateExpandIcon();
18048         this.ctNode.style.display = "";
18049     },
18050
18051     focus : function(){
18052         if(!this.node.preventHScroll){
18053             try{this.anchor.focus();
18054             }catch(e){}
18055         }else if(!Roo.isIE){
18056             try{
18057                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
18058                 var l = noscroll.scrollLeft;
18059                 this.anchor.focus();
18060                 noscroll.scrollLeft = l;
18061             }catch(e){}
18062         }
18063     },
18064
18065     toggleCheck : function(value){
18066         var cb = this.checkbox;
18067         if(cb){
18068             cb.checked = (value === undefined ? !cb.checked : value);
18069         }
18070     },
18071
18072     blur : function(){
18073         try{
18074             this.anchor.blur();
18075         }catch(e){}
18076     },
18077
18078     animExpand : function(callback){
18079         var ct = Roo.get(this.ctNode);
18080         ct.stopFx();
18081         if(!this.node.hasChildNodes()){
18082             this.updateExpandIcon();
18083             this.ctNode.style.display = "";
18084             Roo.callback(callback);
18085             return;
18086         }
18087         this.animating = true;
18088         this.updateExpandIcon();
18089
18090         ct.slideIn('t', {
18091            callback : function(){
18092                this.animating = false;
18093                Roo.callback(callback);
18094             },
18095             scope: this,
18096             duration: this.node.ownerTree.duration || .25
18097         });
18098     },
18099
18100     highlight : function(){
18101         var tree = this.node.getOwnerTree();
18102         Roo.fly(this.wrap).highlight(
18103             tree.hlColor || "C3DAF9",
18104             {endColor: tree.hlBaseColor}
18105         );
18106     },
18107
18108     collapse : function(){
18109         this.updateExpandIcon();
18110         this.ctNode.style.display = "none";
18111     },
18112
18113     animCollapse : function(callback){
18114         var ct = Roo.get(this.ctNode);
18115         ct.enableDisplayMode('block');
18116         ct.stopFx();
18117
18118         this.animating = true;
18119         this.updateExpandIcon();
18120
18121         ct.slideOut('t', {
18122             callback : function(){
18123                this.animating = false;
18124                Roo.callback(callback);
18125             },
18126             scope: this,
18127             duration: this.node.ownerTree.duration || .25
18128         });
18129     },
18130
18131     getContainer : function(){
18132         return this.ctNode;
18133     },
18134
18135     getEl : function(){
18136         return this.wrap;
18137     },
18138
18139     appendDDGhost : function(ghostNode){
18140         ghostNode.appendChild(this.elNode.cloneNode(true));
18141     },
18142
18143     getDDRepairXY : function(){
18144         return Roo.lib.Dom.getXY(this.iconNode);
18145     },
18146
18147     onRender : function(){
18148         this.render();
18149     },
18150
18151     render : function(bulkRender){
18152         var n = this.node, a = n.attributes;
18153         var targetNode = n.parentNode ?
18154               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
18155
18156         if(!this.rendered){
18157             this.rendered = true;
18158
18159             this.renderElements(n, a, targetNode, bulkRender);
18160
18161             if(a.qtip){
18162                if(this.textNode.setAttributeNS){
18163                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
18164                    if(a.qtipTitle){
18165                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
18166                    }
18167                }else{
18168                    this.textNode.setAttribute("ext:qtip", a.qtip);
18169                    if(a.qtipTitle){
18170                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
18171                    }
18172                }
18173             }else if(a.qtipCfg){
18174                 a.qtipCfg.target = Roo.id(this.textNode);
18175                 Roo.QuickTips.register(a.qtipCfg);
18176             }
18177             this.initEvents();
18178             if(!this.node.expanded){
18179                 this.updateExpandIcon();
18180             }
18181         }else{
18182             if(bulkRender === true) {
18183                 targetNode.appendChild(this.wrap);
18184             }
18185         }
18186     },
18187
18188     renderElements : function(n, a, targetNode, bulkRender)
18189     {
18190         // add some indent caching, this helps performance when rendering a large tree
18191         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18192         var t = n.getOwnerTree();
18193         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18194         if (typeof(n.attributes.html) != 'undefined') {
18195             txt = n.attributes.html;
18196         }
18197         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18198         var cb = typeof a.checked == 'boolean';
18199         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18200         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18201             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18202             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18203             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18204             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18205             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18206              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
18207                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18208             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18209             "</li>"];
18210
18211         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18212             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18213                                 n.nextSibling.ui.getEl(), buf.join(""));
18214         }else{
18215             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18216         }
18217
18218         this.elNode = this.wrap.childNodes[0];
18219         this.ctNode = this.wrap.childNodes[1];
18220         var cs = this.elNode.childNodes;
18221         this.indentNode = cs[0];
18222         this.ecNode = cs[1];
18223         this.iconNode = cs[2];
18224         var index = 3;
18225         if(cb){
18226             this.checkbox = cs[3];
18227             index++;
18228         }
18229         this.anchor = cs[index];
18230         this.textNode = cs[index].firstChild;
18231     },
18232
18233     getAnchor : function(){
18234         return this.anchor;
18235     },
18236
18237     getTextEl : function(){
18238         return this.textNode;
18239     },
18240
18241     getIconEl : function(){
18242         return this.iconNode;
18243     },
18244
18245     isChecked : function(){
18246         return this.checkbox ? this.checkbox.checked : false;
18247     },
18248
18249     updateExpandIcon : function(){
18250         if(this.rendered){
18251             var n = this.node, c1, c2;
18252             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18253             var hasChild = n.hasChildNodes();
18254             if(hasChild){
18255                 if(n.expanded){
18256                     cls += "-minus";
18257                     c1 = "x-tree-node-collapsed";
18258                     c2 = "x-tree-node-expanded";
18259                 }else{
18260                     cls += "-plus";
18261                     c1 = "x-tree-node-expanded";
18262                     c2 = "x-tree-node-collapsed";
18263                 }
18264                 if(this.wasLeaf){
18265                     this.removeClass("x-tree-node-leaf");
18266                     this.wasLeaf = false;
18267                 }
18268                 if(this.c1 != c1 || this.c2 != c2){
18269                     Roo.fly(this.elNode).replaceClass(c1, c2);
18270                     this.c1 = c1; this.c2 = c2;
18271                 }
18272             }else{
18273                 // this changes non-leafs into leafs if they have no children.
18274                 // it's not very rational behaviour..
18275                 
18276                 if(!this.wasLeaf && this.node.leaf){
18277                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18278                     delete this.c1;
18279                     delete this.c2;
18280                     this.wasLeaf = true;
18281                 }
18282             }
18283             var ecc = "x-tree-ec-icon "+cls;
18284             if(this.ecc != ecc){
18285                 this.ecNode.className = ecc;
18286                 this.ecc = ecc;
18287             }
18288         }
18289     },
18290
18291     getChildIndent : function(){
18292         if(!this.childIndent){
18293             var buf = [];
18294             var p = this.node;
18295             while(p){
18296                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18297                     if(!p.isLast()) {
18298                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18299                     } else {
18300                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18301                     }
18302                 }
18303                 p = p.parentNode;
18304             }
18305             this.childIndent = buf.join("");
18306         }
18307         return this.childIndent;
18308     },
18309
18310     renderIndent : function(){
18311         if(this.rendered){
18312             var indent = "";
18313             var p = this.node.parentNode;
18314             if(p){
18315                 indent = p.ui.getChildIndent();
18316             }
18317             if(this.indentMarkup != indent){ // don't rerender if not required
18318                 this.indentNode.innerHTML = indent;
18319                 this.indentMarkup = indent;
18320             }
18321             this.updateExpandIcon();
18322         }
18323     }
18324 };
18325
18326 Roo.tree.RootTreeNodeUI = function(){
18327     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18328 };
18329 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18330     render : function(){
18331         if(!this.rendered){
18332             var targetNode = this.node.ownerTree.innerCt.dom;
18333             this.node.expanded = true;
18334             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18335             this.wrap = this.ctNode = targetNode.firstChild;
18336         }
18337     },
18338     collapse : function(){
18339     },
18340     expand : function(){
18341     }
18342 });/*
18343  * Based on:
18344  * Ext JS Library 1.1.1
18345  * Copyright(c) 2006-2007, Ext JS, LLC.
18346  *
18347  * Originally Released Under LGPL - original licence link has changed is not relivant.
18348  *
18349  * Fork - LGPL
18350  * <script type="text/javascript">
18351  */
18352 /**
18353  * @class Roo.tree.TreeLoader
18354  * @extends Roo.util.Observable
18355  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18356  * nodes from a specified URL. The response must be a javascript Array definition
18357  * who's elements are node definition objects. eg:
18358  * <pre><code>
18359    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
18360     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
18361 </code></pre>
18362  * <br><br>
18363  * A server request is sent, and child nodes are loaded only when a node is expanded.
18364  * The loading node's id is passed to the server under the parameter name "node" to
18365  * enable the server to produce the correct child nodes.
18366  * <br><br>
18367  * To pass extra parameters, an event handler may be attached to the "beforeload"
18368  * event, and the parameters specified in the TreeLoader's baseParams property:
18369  * <pre><code>
18370     myTreeLoader.on("beforeload", function(treeLoader, node) {
18371         this.baseParams.category = node.attributes.category;
18372     }, this);
18373 </code></pre><
18374  * This would pass an HTTP parameter called "category" to the server containing
18375  * the value of the Node's "category" attribute.
18376  * @constructor
18377  * Creates a new Treeloader.
18378  * @param {Object} config A config object containing config properties.
18379  */
18380 Roo.tree.TreeLoader = function(config){
18381     this.baseParams = {};
18382     this.requestMethod = "POST";
18383     Roo.apply(this, config);
18384
18385     this.addEvents({
18386     
18387         /**
18388          * @event beforeload
18389          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18390          * @param {Object} This TreeLoader object.
18391          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18392          * @param {Object} callback The callback function specified in the {@link #load} call.
18393          */
18394         beforeload : true,
18395         /**
18396          * @event load
18397          * Fires when the node has been successfuly loaded.
18398          * @param {Object} This TreeLoader object.
18399          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18400          * @param {Object} response The response object containing the data from the server.
18401          */
18402         load : true,
18403         /**
18404          * @event loadexception
18405          * Fires if the network request failed.
18406          * @param {Object} This TreeLoader object.
18407          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18408          * @param {Object} response The response object containing the data from the server.
18409          */
18410         loadexception : true,
18411         /**
18412          * @event create
18413          * Fires before a node is created, enabling you to return custom Node types 
18414          * @param {Object} This TreeLoader object.
18415          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18416          */
18417         create : true
18418     });
18419
18420     Roo.tree.TreeLoader.superclass.constructor.call(this);
18421 };
18422
18423 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18424     /**
18425     * @cfg {String} dataUrl The URL from which to request a Json string which
18426     * specifies an array of node definition object representing the child nodes
18427     * to be loaded.
18428     */
18429     /**
18430     * @cfg {Object} baseParams (optional) An object containing properties which
18431     * specify HTTP parameters to be passed to each request for child nodes.
18432     */
18433     /**
18434     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18435     * created by this loader. If the attributes sent by the server have an attribute in this object,
18436     * they take priority.
18437     */
18438     /**
18439     * @cfg {Object} uiProviders (optional) An object containing properties which
18440     * 
18441     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
18442     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18443     * <i>uiProvider</i> attribute of a returned child node is a string rather
18444     * than a reference to a TreeNodeUI implementation, this that string value
18445     * is used as a property name in the uiProviders object. You can define the provider named
18446     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18447     */
18448     uiProviders : {},
18449
18450     /**
18451     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18452     * child nodes before loading.
18453     */
18454     clearOnLoad : true,
18455
18456     /**
18457     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18458     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18459     * Grid query { data : [ .....] }
18460     */
18461     
18462     root : false,
18463      /**
18464     * @cfg {String} queryParam (optional) 
18465     * Name of the query as it will be passed on the querystring (defaults to 'node')
18466     * eg. the request will be ?node=[id]
18467     */
18468     
18469     
18470     queryParam: false,
18471     
18472     /**
18473      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18474      * This is called automatically when a node is expanded, but may be used to reload
18475      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18476      * @param {Roo.tree.TreeNode} node
18477      * @param {Function} callback
18478      */
18479     load : function(node, callback){
18480         if(this.clearOnLoad){
18481             while(node.firstChild){
18482                 node.removeChild(node.firstChild);
18483             }
18484         }
18485         if(node.attributes.children){ // preloaded json children
18486             var cs = node.attributes.children;
18487             for(var i = 0, len = cs.length; i < len; i++){
18488                 node.appendChild(this.createNode(cs[i]));
18489             }
18490             if(typeof callback == "function"){
18491                 callback();
18492             }
18493         }else if(this.dataUrl){
18494             this.requestData(node, callback);
18495         }
18496     },
18497
18498     getParams: function(node){
18499         var buf = [], bp = this.baseParams;
18500         for(var key in bp){
18501             if(typeof bp[key] != "function"){
18502                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18503             }
18504         }
18505         var n = this.queryParam === false ? 'node' : this.queryParam;
18506         buf.push(n + "=", encodeURIComponent(node.id));
18507         return buf.join("");
18508     },
18509
18510     requestData : function(node, callback){
18511         if(this.fireEvent("beforeload", this, node, callback) !== false){
18512             this.transId = Roo.Ajax.request({
18513                 method:this.requestMethod,
18514                 url: this.dataUrl||this.url,
18515                 success: this.handleResponse,
18516                 failure: this.handleFailure,
18517                 scope: this,
18518                 argument: {callback: callback, node: node},
18519                 params: this.getParams(node)
18520             });
18521         }else{
18522             // if the load is cancelled, make sure we notify
18523             // the node that we are done
18524             if(typeof callback == "function"){
18525                 callback();
18526             }
18527         }
18528     },
18529
18530     isLoading : function(){
18531         return this.transId ? true : false;
18532     },
18533
18534     abort : function(){
18535         if(this.isLoading()){
18536             Roo.Ajax.abort(this.transId);
18537         }
18538     },
18539
18540     // private
18541     createNode : function(attr)
18542     {
18543         // apply baseAttrs, nice idea Corey!
18544         if(this.baseAttrs){
18545             Roo.applyIf(attr, this.baseAttrs);
18546         }
18547         if(this.applyLoader !== false){
18548             attr.loader = this;
18549         }
18550         // uiProvider = depreciated..
18551         
18552         if(typeof(attr.uiProvider) == 'string'){
18553            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18554                 /**  eval:var:attr */ eval(attr.uiProvider);
18555         }
18556         if(typeof(this.uiProviders['default']) != 'undefined') {
18557             attr.uiProvider = this.uiProviders['default'];
18558         }
18559         
18560         this.fireEvent('create', this, attr);
18561         
18562         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18563         return(attr.leaf ?
18564                         new Roo.tree.TreeNode(attr) :
18565                         new Roo.tree.AsyncTreeNode(attr));
18566     },
18567
18568     processResponse : function(response, node, callback)
18569     {
18570         var json = response.responseText;
18571         try {
18572             
18573             var o = Roo.decode(json);
18574             
18575             if (!o.success) {
18576                 // it's a failure condition.
18577                 var a = response.argument;
18578                 this.fireEvent("loadexception", this, a.node, response);
18579                 Roo.log("Load failed - should have a handler really");
18580                 return;
18581             }
18582             
18583             if (this.root !== false) {
18584                 o = o[this.root];
18585             }
18586             
18587             for(var i = 0, len = o.length; i < len; i++){
18588                 var n = this.createNode(o[i]);
18589                 if(n){
18590                     node.appendChild(n);
18591                 }
18592             }
18593             if(typeof callback == "function"){
18594                 callback(this, node);
18595             }
18596         }catch(e){
18597             this.handleFailure(response);
18598         }
18599     },
18600
18601     handleResponse : function(response){
18602         this.transId = false;
18603         var a = response.argument;
18604         this.processResponse(response, a.node, a.callback);
18605         this.fireEvent("load", this, a.node, response);
18606     },
18607
18608     handleFailure : function(response)
18609     {
18610         // should handle failure better..
18611         this.transId = false;
18612         var a = response.argument;
18613         this.fireEvent("loadexception", this, a.node, response);
18614         if(typeof a.callback == "function"){
18615             a.callback(this, a.node);
18616         }
18617     }
18618 });/*
18619  * Based on:
18620  * Ext JS Library 1.1.1
18621  * Copyright(c) 2006-2007, Ext JS, LLC.
18622  *
18623  * Originally Released Under LGPL - original licence link has changed is not relivant.
18624  *
18625  * Fork - LGPL
18626  * <script type="text/javascript">
18627  */
18628
18629 /**
18630 * @class Roo.tree.TreeFilter
18631 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18632 * @param {TreePanel} tree
18633 * @param {Object} config (optional)
18634  */
18635 Roo.tree.TreeFilter = function(tree, config){
18636     this.tree = tree;
18637     this.filtered = {};
18638     Roo.apply(this, config);
18639 };
18640
18641 Roo.tree.TreeFilter.prototype = {
18642     clearBlank:false,
18643     reverse:false,
18644     autoClear:false,
18645     remove:false,
18646
18647      /**
18648      * Filter the data by a specific attribute.
18649      * @param {String/RegExp} value Either string that the attribute value
18650      * should start with or a RegExp to test against the attribute
18651      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18652      * @param {TreeNode} startNode (optional) The node to start the filter at.
18653      */
18654     filter : function(value, attr, startNode){
18655         attr = attr || "text";
18656         var f;
18657         if(typeof value == "string"){
18658             var vlen = value.length;
18659             // auto clear empty filter
18660             if(vlen == 0 && this.clearBlank){
18661                 this.clear();
18662                 return;
18663             }
18664             value = value.toLowerCase();
18665             f = function(n){
18666                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18667             };
18668         }else if(value.exec){ // regex?
18669             f = function(n){
18670                 return value.test(n.attributes[attr]);
18671             };
18672         }else{
18673             throw 'Illegal filter type, must be string or regex';
18674         }
18675         this.filterBy(f, null, startNode);
18676         },
18677
18678     /**
18679      * Filter by a function. The passed function will be called with each
18680      * node in the tree (or from the startNode). If the function returns true, the node is kept
18681      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18682      * @param {Function} fn The filter function
18683      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18684      */
18685     filterBy : function(fn, scope, startNode){
18686         startNode = startNode || this.tree.root;
18687         if(this.autoClear){
18688             this.clear();
18689         }
18690         var af = this.filtered, rv = this.reverse;
18691         var f = function(n){
18692             if(n == startNode){
18693                 return true;
18694             }
18695             if(af[n.id]){
18696                 return false;
18697             }
18698             var m = fn.call(scope || n, n);
18699             if(!m || rv){
18700                 af[n.id] = n;
18701                 n.ui.hide();
18702                 return false;
18703             }
18704             return true;
18705         };
18706         startNode.cascade(f);
18707         if(this.remove){
18708            for(var id in af){
18709                if(typeof id != "function"){
18710                    var n = af[id];
18711                    if(n && n.parentNode){
18712                        n.parentNode.removeChild(n);
18713                    }
18714                }
18715            }
18716         }
18717     },
18718
18719     /**
18720      * Clears the current filter. Note: with the "remove" option
18721      * set a filter cannot be cleared.
18722      */
18723     clear : function(){
18724         var t = this.tree;
18725         var af = this.filtered;
18726         for(var id in af){
18727             if(typeof id != "function"){
18728                 var n = af[id];
18729                 if(n){
18730                     n.ui.show();
18731                 }
18732             }
18733         }
18734         this.filtered = {};
18735     }
18736 };
18737 /*
18738  * Based on:
18739  * Ext JS Library 1.1.1
18740  * Copyright(c) 2006-2007, Ext JS, LLC.
18741  *
18742  * Originally Released Under LGPL - original licence link has changed is not relivant.
18743  *
18744  * Fork - LGPL
18745  * <script type="text/javascript">
18746  */
18747  
18748
18749 /**
18750  * @class Roo.tree.TreeSorter
18751  * Provides sorting of nodes in a TreePanel
18752  * 
18753  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18754  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18755  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18756  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18757  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18758  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18759  * @constructor
18760  * @param {TreePanel} tree
18761  * @param {Object} config
18762  */
18763 Roo.tree.TreeSorter = function(tree, config){
18764     Roo.apply(this, config);
18765     tree.on("beforechildrenrendered", this.doSort, this);
18766     tree.on("append", this.updateSort, this);
18767     tree.on("insert", this.updateSort, this);
18768     
18769     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18770     var p = this.property || "text";
18771     var sortType = this.sortType;
18772     var fs = this.folderSort;
18773     var cs = this.caseSensitive === true;
18774     var leafAttr = this.leafAttr || 'leaf';
18775
18776     this.sortFn = function(n1, n2){
18777         if(fs){
18778             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18779                 return 1;
18780             }
18781             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18782                 return -1;
18783             }
18784         }
18785         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18786         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18787         if(v1 < v2){
18788                         return dsc ? +1 : -1;
18789                 }else if(v1 > v2){
18790                         return dsc ? -1 : +1;
18791         }else{
18792                 return 0;
18793         }
18794     };
18795 };
18796
18797 Roo.tree.TreeSorter.prototype = {
18798     doSort : function(node){
18799         node.sort(this.sortFn);
18800     },
18801     
18802     compareNodes : function(n1, n2){
18803         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18804     },
18805     
18806     updateSort : function(tree, node){
18807         if(node.childrenRendered){
18808             this.doSort.defer(1, this, [node]);
18809         }
18810     }
18811 };/*
18812  * Based on:
18813  * Ext JS Library 1.1.1
18814  * Copyright(c) 2006-2007, Ext JS, LLC.
18815  *
18816  * Originally Released Under LGPL - original licence link has changed is not relivant.
18817  *
18818  * Fork - LGPL
18819  * <script type="text/javascript">
18820  */
18821
18822 if(Roo.dd.DropZone){
18823     
18824 Roo.tree.TreeDropZone = function(tree, config){
18825     this.allowParentInsert = false;
18826     this.allowContainerDrop = false;
18827     this.appendOnly = false;
18828     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18829     this.tree = tree;
18830     this.lastInsertClass = "x-tree-no-status";
18831     this.dragOverData = {};
18832 };
18833
18834 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18835     ddGroup : "TreeDD",
18836     
18837     expandDelay : 1000,
18838     
18839     expandNode : function(node){
18840         if(node.hasChildNodes() && !node.isExpanded()){
18841             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18842         }
18843     },
18844     
18845     queueExpand : function(node){
18846         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18847     },
18848     
18849     cancelExpand : function(){
18850         if(this.expandProcId){
18851             clearTimeout(this.expandProcId);
18852             this.expandProcId = false;
18853         }
18854     },
18855     
18856     isValidDropPoint : function(n, pt, dd, e, data){
18857         if(!n || !data){ return false; }
18858         var targetNode = n.node;
18859         var dropNode = data.node;
18860         // default drop rules
18861         if(!(targetNode && targetNode.isTarget && pt)){
18862             return false;
18863         }
18864         if(pt == "append" && targetNode.allowChildren === false){
18865             return false;
18866         }
18867         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18868             return false;
18869         }
18870         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18871             return false;
18872         }
18873         // reuse the object
18874         var overEvent = this.dragOverData;
18875         overEvent.tree = this.tree;
18876         overEvent.target = targetNode;
18877         overEvent.data = data;
18878         overEvent.point = pt;
18879         overEvent.source = dd;
18880         overEvent.rawEvent = e;
18881         overEvent.dropNode = dropNode;
18882         overEvent.cancel = false;  
18883         var result = this.tree.fireEvent("nodedragover", overEvent);
18884         return overEvent.cancel === false && result !== false;
18885     },
18886     
18887     getDropPoint : function(e, n, dd){
18888         var tn = n.node;
18889         if(tn.isRoot){
18890             return tn.allowChildren !== false ? "append" : false; // always append for root
18891         }
18892         var dragEl = n.ddel;
18893         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18894         var y = Roo.lib.Event.getPageY(e);
18895         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18896         
18897         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18898         var noAppend = tn.allowChildren === false;
18899         if(this.appendOnly || tn.parentNode.allowChildren === false){
18900             return noAppend ? false : "append";
18901         }
18902         var noBelow = false;
18903         if(!this.allowParentInsert){
18904             noBelow = tn.hasChildNodes() && tn.isExpanded();
18905         }
18906         var q = (b - t) / (noAppend ? 2 : 3);
18907         if(y >= t && y < (t + q)){
18908             return "above";
18909         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18910             return "below";
18911         }else{
18912             return "append";
18913         }
18914     },
18915     
18916     onNodeEnter : function(n, dd, e, data){
18917         this.cancelExpand();
18918     },
18919     
18920     onNodeOver : function(n, dd, e, data){
18921         var pt = this.getDropPoint(e, n, dd);
18922         var node = n.node;
18923         
18924         // auto node expand check
18925         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18926             this.queueExpand(node);
18927         }else if(pt != "append"){
18928             this.cancelExpand();
18929         }
18930         
18931         // set the insert point style on the target node
18932         var returnCls = this.dropNotAllowed;
18933         if(this.isValidDropPoint(n, pt, dd, e, data)){
18934            if(pt){
18935                var el = n.ddel;
18936                var cls;
18937                if(pt == "above"){
18938                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18939                    cls = "x-tree-drag-insert-above";
18940                }else if(pt == "below"){
18941                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18942                    cls = "x-tree-drag-insert-below";
18943                }else{
18944                    returnCls = "x-tree-drop-ok-append";
18945                    cls = "x-tree-drag-append";
18946                }
18947                if(this.lastInsertClass != cls){
18948                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18949                    this.lastInsertClass = cls;
18950                }
18951            }
18952        }
18953        return returnCls;
18954     },
18955     
18956     onNodeOut : function(n, dd, e, data){
18957         this.cancelExpand();
18958         this.removeDropIndicators(n);
18959     },
18960     
18961     onNodeDrop : function(n, dd, e, data){
18962         var point = this.getDropPoint(e, n, dd);
18963         var targetNode = n.node;
18964         targetNode.ui.startDrop();
18965         if(!this.isValidDropPoint(n, point, dd, e, data)){
18966             targetNode.ui.endDrop();
18967             return false;
18968         }
18969         // first try to find the drop node
18970         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
18971         var dropEvent = {
18972             tree : this.tree,
18973             target: targetNode,
18974             data: data,
18975             point: point,
18976             source: dd,
18977             rawEvent: e,
18978             dropNode: dropNode,
18979             cancel: !dropNode   
18980         };
18981         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
18982         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
18983             targetNode.ui.endDrop();
18984             return false;
18985         }
18986         // allow target changing
18987         targetNode = dropEvent.target;
18988         if(point == "append" && !targetNode.isExpanded()){
18989             targetNode.expand(false, null, function(){
18990                 this.completeDrop(dropEvent);
18991             }.createDelegate(this));
18992         }else{
18993             this.completeDrop(dropEvent);
18994         }
18995         return true;
18996     },
18997     
18998     completeDrop : function(de){
18999         var ns = de.dropNode, p = de.point, t = de.target;
19000         if(!(ns instanceof Array)){
19001             ns = [ns];
19002         }
19003         var n;
19004         for(var i = 0, len = ns.length; i < len; i++){
19005             n = ns[i];
19006             if(p == "above"){
19007                 t.parentNode.insertBefore(n, t);
19008             }else if(p == "below"){
19009                 t.parentNode.insertBefore(n, t.nextSibling);
19010             }else{
19011                 t.appendChild(n);
19012             }
19013         }
19014         n.ui.focus();
19015         if(this.tree.hlDrop){
19016             n.ui.highlight();
19017         }
19018         t.ui.endDrop();
19019         this.tree.fireEvent("nodedrop", de);
19020     },
19021     
19022     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
19023         if(this.tree.hlDrop){
19024             dropNode.ui.focus();
19025             dropNode.ui.highlight();
19026         }
19027         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
19028     },
19029     
19030     getTree : function(){
19031         return this.tree;
19032     },
19033     
19034     removeDropIndicators : function(n){
19035         if(n && n.ddel){
19036             var el = n.ddel;
19037             Roo.fly(el).removeClass([
19038                     "x-tree-drag-insert-above",
19039                     "x-tree-drag-insert-below",
19040                     "x-tree-drag-append"]);
19041             this.lastInsertClass = "_noclass";
19042         }
19043     },
19044     
19045     beforeDragDrop : function(target, e, id){
19046         this.cancelExpand();
19047         return true;
19048     },
19049     
19050     afterRepair : function(data){
19051         if(data && Roo.enableFx){
19052             data.node.ui.highlight();
19053         }
19054         this.hideProxy();
19055     }    
19056 });
19057
19058 }
19059 /*
19060  * Based on:
19061  * Ext JS Library 1.1.1
19062  * Copyright(c) 2006-2007, Ext JS, LLC.
19063  *
19064  * Originally Released Under LGPL - original licence link has changed is not relivant.
19065  *
19066  * Fork - LGPL
19067  * <script type="text/javascript">
19068  */
19069  
19070
19071 if(Roo.dd.DragZone){
19072 Roo.tree.TreeDragZone = function(tree, config){
19073     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
19074     this.tree = tree;
19075 };
19076
19077 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
19078     ddGroup : "TreeDD",
19079     
19080     onBeforeDrag : function(data, e){
19081         var n = data.node;
19082         return n && n.draggable && !n.disabled;
19083     },
19084     
19085     onInitDrag : function(e){
19086         var data = this.dragData;
19087         this.tree.getSelectionModel().select(data.node);
19088         this.proxy.update("");
19089         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
19090         this.tree.fireEvent("startdrag", this.tree, data.node, e);
19091     },
19092     
19093     getRepairXY : function(e, data){
19094         return data.node.ui.getDDRepairXY();
19095     },
19096     
19097     onEndDrag : function(data, e){
19098         this.tree.fireEvent("enddrag", this.tree, data.node, e);
19099     },
19100     
19101     onValidDrop : function(dd, e, id){
19102         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
19103         this.hideProxy();
19104     },
19105     
19106     beforeInvalidDrop : function(e, id){
19107         // this scrolls the original position back into view
19108         var sm = this.tree.getSelectionModel();
19109         sm.clearSelections();
19110         sm.select(this.dragData.node);
19111     }
19112 });
19113 }/*
19114  * Based on:
19115  * Ext JS Library 1.1.1
19116  * Copyright(c) 2006-2007, Ext JS, LLC.
19117  *
19118  * Originally Released Under LGPL - original licence link has changed is not relivant.
19119  *
19120  * Fork - LGPL
19121  * <script type="text/javascript">
19122  */
19123 /**
19124  * @class Roo.tree.TreeEditor
19125  * @extends Roo.Editor
19126  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
19127  * as the editor field.
19128  * @constructor
19129  * @param {Object} config (used to be the tree panel.)
19130  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
19131  * 
19132  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
19133  * @cfg {Roo.form.TextField|Object} field The field configuration
19134  *
19135  * 
19136  */
19137 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
19138     var tree = config;
19139     var field;
19140     if (oldconfig) { // old style..
19141         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
19142     } else {
19143         // new style..
19144         tree = config.tree;
19145         config.field = config.field  || {};
19146         config.field.xtype = 'TextField';
19147         field = Roo.factory(config.field, Roo.form);
19148     }
19149     config = config || {};
19150     
19151     
19152     this.addEvents({
19153         /**
19154          * @event beforenodeedit
19155          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
19156          * false from the handler of this event.
19157          * @param {Editor} this
19158          * @param {Roo.tree.Node} node 
19159          */
19160         "beforenodeedit" : true
19161     });
19162     
19163     //Roo.log(config);
19164     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
19165
19166     this.tree = tree;
19167
19168     tree.on('beforeclick', this.beforeNodeClick, this);
19169     tree.getTreeEl().on('mousedown', this.hide, this);
19170     this.on('complete', this.updateNode, this);
19171     this.on('beforestartedit', this.fitToTree, this);
19172     this.on('startedit', this.bindScroll, this, {delay:10});
19173     this.on('specialkey', this.onSpecialKey, this);
19174 };
19175
19176 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
19177     /**
19178      * @cfg {String} alignment
19179      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
19180      */
19181     alignment: "l-l",
19182     // inherit
19183     autoSize: false,
19184     /**
19185      * @cfg {Boolean} hideEl
19186      * True to hide the bound element while the editor is displayed (defaults to false)
19187      */
19188     hideEl : false,
19189     /**
19190      * @cfg {String} cls
19191      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
19192      */
19193     cls: "x-small-editor x-tree-editor",
19194     /**
19195      * @cfg {Boolean} shim
19196      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
19197      */
19198     shim:false,
19199     // inherit
19200     shadow:"frame",
19201     /**
19202      * @cfg {Number} maxWidth
19203      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
19204      * the containing tree element's size, it will be automatically limited for you to the container width, taking
19205      * scroll and client offsets into account prior to each edit.
19206      */
19207     maxWidth: 250,
19208
19209     editDelay : 350,
19210
19211     // private
19212     fitToTree : function(ed, el){
19213         var td = this.tree.getTreeEl().dom, nd = el.dom;
19214         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
19215             td.scrollLeft = nd.offsetLeft;
19216         }
19217         var w = Math.min(
19218                 this.maxWidth,
19219                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
19220         this.setSize(w, '');
19221         
19222         return this.fireEvent('beforenodeedit', this, this.editNode);
19223         
19224     },
19225
19226     // private
19227     triggerEdit : function(node){
19228         this.completeEdit();
19229         this.editNode = node;
19230         this.startEdit(node.ui.textNode, node.text);
19231     },
19232
19233     // private
19234     bindScroll : function(){
19235         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
19236     },
19237
19238     // private
19239     beforeNodeClick : function(node, e){
19240         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19241         this.lastClick = new Date();
19242         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19243             e.stopEvent();
19244             this.triggerEdit(node);
19245             return false;
19246         }
19247         return true;
19248     },
19249
19250     // private
19251     updateNode : function(ed, value){
19252         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19253         this.editNode.setText(value);
19254     },
19255
19256     // private
19257     onHide : function(){
19258         Roo.tree.TreeEditor.superclass.onHide.call(this);
19259         if(this.editNode){
19260             this.editNode.ui.focus();
19261         }
19262     },
19263
19264     // private
19265     onSpecialKey : function(field, e){
19266         var k = e.getKey();
19267         if(k == e.ESC){
19268             e.stopEvent();
19269             this.cancelEdit();
19270         }else if(k == e.ENTER && !e.hasModifier()){
19271             e.stopEvent();
19272             this.completeEdit();
19273         }
19274     }
19275 });//<Script type="text/javascript">
19276 /*
19277  * Based on:
19278  * Ext JS Library 1.1.1
19279  * Copyright(c) 2006-2007, Ext JS, LLC.
19280  *
19281  * Originally Released Under LGPL - original licence link has changed is not relivant.
19282  *
19283  * Fork - LGPL
19284  * <script type="text/javascript">
19285  */
19286  
19287 /**
19288  * Not documented??? - probably should be...
19289  */
19290
19291 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19292     //focus: Roo.emptyFn, // prevent odd scrolling behavior
19293     
19294     renderElements : function(n, a, targetNode, bulkRender){
19295         //consel.log("renderElements?");
19296         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19297
19298         var t = n.getOwnerTree();
19299         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19300         
19301         var cols = t.columns;
19302         var bw = t.borderWidth;
19303         var c = cols[0];
19304         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19305          var cb = typeof a.checked == "boolean";
19306         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19307         var colcls = 'x-t-' + tid + '-c0';
19308         var buf = [
19309             '<li class="x-tree-node">',
19310             
19311                 
19312                 '<div class="x-tree-node-el ', a.cls,'">',
19313                     // extran...
19314                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19315                 
19316                 
19317                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19318                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
19319                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19320                            (a.icon ? ' x-tree-node-inline-icon' : ''),
19321                            (a.iconCls ? ' '+a.iconCls : ''),
19322                            '" unselectable="on" />',
19323                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
19324                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
19325                              
19326                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19327                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19328                             '<span unselectable="on" qtip="' + tx + '">',
19329                              tx,
19330                              '</span></a>' ,
19331                     '</div>',
19332                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19333                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19334                  ];
19335         for(var i = 1, len = cols.length; i < len; i++){
19336             c = cols[i];
19337             colcls = 'x-t-' + tid + '-c' +i;
19338             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19339             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19340                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19341                       "</div>");
19342          }
19343          
19344          buf.push(
19345             '</a>',
19346             '<div class="x-clear"></div></div>',
19347             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19348             "</li>");
19349         
19350         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19351             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19352                                 n.nextSibling.ui.getEl(), buf.join(""));
19353         }else{
19354             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19355         }
19356         var el = this.wrap.firstChild;
19357         this.elRow = el;
19358         this.elNode = el.firstChild;
19359         this.ranchor = el.childNodes[1];
19360         this.ctNode = this.wrap.childNodes[1];
19361         var cs = el.firstChild.childNodes;
19362         this.indentNode = cs[0];
19363         this.ecNode = cs[1];
19364         this.iconNode = cs[2];
19365         var index = 3;
19366         if(cb){
19367             this.checkbox = cs[3];
19368             index++;
19369         }
19370         this.anchor = cs[index];
19371         
19372         this.textNode = cs[index].firstChild;
19373         
19374         //el.on("click", this.onClick, this);
19375         //el.on("dblclick", this.onDblClick, this);
19376         
19377         
19378        // console.log(this);
19379     },
19380     initEvents : function(){
19381         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19382         
19383             
19384         var a = this.ranchor;
19385
19386         var el = Roo.get(a);
19387
19388         if(Roo.isOpera){ // opera render bug ignores the CSS
19389             el.setStyle("text-decoration", "none");
19390         }
19391
19392         el.on("click", this.onClick, this);
19393         el.on("dblclick", this.onDblClick, this);
19394         el.on("contextmenu", this.onContextMenu, this);
19395         
19396     },
19397     
19398     /*onSelectedChange : function(state){
19399         if(state){
19400             this.focus();
19401             this.addClass("x-tree-selected");
19402         }else{
19403             //this.blur();
19404             this.removeClass("x-tree-selected");
19405         }
19406     },*/
19407     addClass : function(cls){
19408         if(this.elRow){
19409             Roo.fly(this.elRow).addClass(cls);
19410         }
19411         
19412     },
19413     
19414     
19415     removeClass : function(cls){
19416         if(this.elRow){
19417             Roo.fly(this.elRow).removeClass(cls);
19418         }
19419     }
19420
19421     
19422     
19423 });//<Script type="text/javascript">
19424
19425 /*
19426  * Based on:
19427  * Ext JS Library 1.1.1
19428  * Copyright(c) 2006-2007, Ext JS, LLC.
19429  *
19430  * Originally Released Under LGPL - original licence link has changed is not relivant.
19431  *
19432  * Fork - LGPL
19433  * <script type="text/javascript">
19434  */
19435  
19436
19437 /**
19438  * @class Roo.tree.ColumnTree
19439  * @extends Roo.data.TreePanel
19440  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19441  * @cfg {int} borderWidth  compined right/left border allowance
19442  * @constructor
19443  * @param {String/HTMLElement/Element} el The container element
19444  * @param {Object} config
19445  */
19446 Roo.tree.ColumnTree =  function(el, config)
19447 {
19448    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19449    this.addEvents({
19450         /**
19451         * @event resize
19452         * Fire this event on a container when it resizes
19453         * @param {int} w Width
19454         * @param {int} h Height
19455         */
19456        "resize" : true
19457     });
19458     this.on('resize', this.onResize, this);
19459 };
19460
19461 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19462     //lines:false,
19463     
19464     
19465     borderWidth: Roo.isBorderBox ? 0 : 2, 
19466     headEls : false,
19467     
19468     render : function(){
19469         // add the header.....
19470        
19471         Roo.tree.ColumnTree.superclass.render.apply(this);
19472         
19473         this.el.addClass('x-column-tree');
19474         
19475         this.headers = this.el.createChild(
19476             {cls:'x-tree-headers'},this.innerCt.dom);
19477    
19478         var cols = this.columns, c;
19479         var totalWidth = 0;
19480         this.headEls = [];
19481         var  len = cols.length;
19482         for(var i = 0; i < len; i++){
19483              c = cols[i];
19484              totalWidth += c.width;
19485             this.headEls.push(this.headers.createChild({
19486                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19487                  cn: {
19488                      cls:'x-tree-hd-text',
19489                      html: c.header
19490                  },
19491                  style:'width:'+(c.width-this.borderWidth)+'px;'
19492              }));
19493         }
19494         this.headers.createChild({cls:'x-clear'});
19495         // prevent floats from wrapping when clipped
19496         this.headers.setWidth(totalWidth);
19497         //this.innerCt.setWidth(totalWidth);
19498         this.innerCt.setStyle({ overflow: 'auto' });
19499         this.onResize(this.width, this.height);
19500              
19501         
19502     },
19503     onResize : function(w,h)
19504     {
19505         this.height = h;
19506         this.width = w;
19507         // resize cols..
19508         this.innerCt.setWidth(this.width);
19509         this.innerCt.setHeight(this.height-20);
19510         
19511         // headers...
19512         var cols = this.columns, c;
19513         var totalWidth = 0;
19514         var expEl = false;
19515         var len = cols.length;
19516         for(var i = 0; i < len; i++){
19517             c = cols[i];
19518             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19519                 // it's the expander..
19520                 expEl  = this.headEls[i];
19521                 continue;
19522             }
19523             totalWidth += c.width;
19524             
19525         }
19526         if (expEl) {
19527             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19528         }
19529         this.headers.setWidth(w-20);
19530
19531         
19532         
19533         
19534     }
19535 });
19536 /*
19537  * Based on:
19538  * Ext JS Library 1.1.1
19539  * Copyright(c) 2006-2007, Ext JS, LLC.
19540  *
19541  * Originally Released Under LGPL - original licence link has changed is not relivant.
19542  *
19543  * Fork - LGPL
19544  * <script type="text/javascript">
19545  */
19546  
19547 /**
19548  * @class Roo.menu.Menu
19549  * @extends Roo.util.Observable
19550  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19551  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19552  * @constructor
19553  * Creates a new Menu
19554  * @param {Object} config Configuration options
19555  */
19556 Roo.menu.Menu = function(config){
19557     Roo.apply(this, config);
19558     this.id = this.id || Roo.id();
19559     this.addEvents({
19560         /**
19561          * @event beforeshow
19562          * Fires before this menu is displayed
19563          * @param {Roo.menu.Menu} this
19564          */
19565         beforeshow : true,
19566         /**
19567          * @event beforehide
19568          * Fires before this menu is hidden
19569          * @param {Roo.menu.Menu} this
19570          */
19571         beforehide : true,
19572         /**
19573          * @event show
19574          * Fires after this menu is displayed
19575          * @param {Roo.menu.Menu} this
19576          */
19577         show : true,
19578         /**
19579          * @event hide
19580          * Fires after this menu is hidden
19581          * @param {Roo.menu.Menu} this
19582          */
19583         hide : true,
19584         /**
19585          * @event click
19586          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19587          * @param {Roo.menu.Menu} this
19588          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19589          * @param {Roo.EventObject} e
19590          */
19591         click : true,
19592         /**
19593          * @event mouseover
19594          * Fires when the mouse is hovering over this menu
19595          * @param {Roo.menu.Menu} this
19596          * @param {Roo.EventObject} e
19597          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19598          */
19599         mouseover : true,
19600         /**
19601          * @event mouseout
19602          * Fires when the mouse exits this menu
19603          * @param {Roo.menu.Menu} this
19604          * @param {Roo.EventObject} e
19605          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19606          */
19607         mouseout : true,
19608         /**
19609          * @event itemclick
19610          * Fires when a menu item contained in this menu is clicked
19611          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19612          * @param {Roo.EventObject} e
19613          */
19614         itemclick: true
19615     });
19616     if (this.registerMenu) {
19617         Roo.menu.MenuMgr.register(this);
19618     }
19619     
19620     var mis = this.items;
19621     this.items = new Roo.util.MixedCollection();
19622     if(mis){
19623         this.add.apply(this, mis);
19624     }
19625 };
19626
19627 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19628     /**
19629      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19630      */
19631     minWidth : 120,
19632     /**
19633      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19634      * for bottom-right shadow (defaults to "sides")
19635      */
19636     shadow : "sides",
19637     /**
19638      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19639      * this menu (defaults to "tl-tr?")
19640      */
19641     subMenuAlign : "tl-tr?",
19642     /**
19643      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19644      * relative to its element of origin (defaults to "tl-bl?")
19645      */
19646     defaultAlign : "tl-bl?",
19647     /**
19648      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19649      */
19650     allowOtherMenus : false,
19651     /**
19652      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19653      */
19654     registerMenu : true,
19655
19656     hidden:true,
19657
19658     // private
19659     render : function(){
19660         if(this.el){
19661             return;
19662         }
19663         var el = this.el = new Roo.Layer({
19664             cls: "x-menu",
19665             shadow:this.shadow,
19666             constrain: false,
19667             parentEl: this.parentEl || document.body,
19668             zindex:15000
19669         });
19670
19671         this.keyNav = new Roo.menu.MenuNav(this);
19672
19673         if(this.plain){
19674             el.addClass("x-menu-plain");
19675         }
19676         if(this.cls){
19677             el.addClass(this.cls);
19678         }
19679         // generic focus element
19680         this.focusEl = el.createChild({
19681             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19682         });
19683         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19684         ul.on("click", this.onClick, this);
19685         ul.on("mouseover", this.onMouseOver, this);
19686         ul.on("mouseout", this.onMouseOut, this);
19687         this.items.each(function(item){
19688             var li = document.createElement("li");
19689             li.className = "x-menu-list-item";
19690             ul.dom.appendChild(li);
19691             item.render(li, this);
19692         }, this);
19693         this.ul = ul;
19694         this.autoWidth();
19695     },
19696
19697     // private
19698     autoWidth : function(){
19699         var el = this.el, ul = this.ul;
19700         if(!el){
19701             return;
19702         }
19703         var w = this.width;
19704         if(w){
19705             el.setWidth(w);
19706         }else if(Roo.isIE){
19707             el.setWidth(this.minWidth);
19708             var t = el.dom.offsetWidth; // force recalc
19709             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19710         }
19711     },
19712
19713     // private
19714     delayAutoWidth : function(){
19715         if(this.rendered){
19716             if(!this.awTask){
19717                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19718             }
19719             this.awTask.delay(20);
19720         }
19721     },
19722
19723     // private
19724     findTargetItem : function(e){
19725         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19726         if(t && t.menuItemId){
19727             return this.items.get(t.menuItemId);
19728         }
19729     },
19730
19731     // private
19732     onClick : function(e){
19733         var t;
19734         if(t = this.findTargetItem(e)){
19735             t.onClick(e);
19736             this.fireEvent("click", this, t, e);
19737         }
19738     },
19739
19740     // private
19741     setActiveItem : function(item, autoExpand){
19742         if(item != this.activeItem){
19743             if(this.activeItem){
19744                 this.activeItem.deactivate();
19745             }
19746             this.activeItem = item;
19747             item.activate(autoExpand);
19748         }else if(autoExpand){
19749             item.expandMenu();
19750         }
19751     },
19752
19753     // private
19754     tryActivate : function(start, step){
19755         var items = this.items;
19756         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19757             var item = items.get(i);
19758             if(!item.disabled && item.canActivate){
19759                 this.setActiveItem(item, false);
19760                 return item;
19761             }
19762         }
19763         return false;
19764     },
19765
19766     // private
19767     onMouseOver : function(e){
19768         var t;
19769         if(t = this.findTargetItem(e)){
19770             if(t.canActivate && !t.disabled){
19771                 this.setActiveItem(t, true);
19772             }
19773         }
19774         this.fireEvent("mouseover", this, e, t);
19775     },
19776
19777     // private
19778     onMouseOut : function(e){
19779         var t;
19780         if(t = this.findTargetItem(e)){
19781             if(t == this.activeItem && t.shouldDeactivate(e)){
19782                 this.activeItem.deactivate();
19783                 delete this.activeItem;
19784             }
19785         }
19786         this.fireEvent("mouseout", this, e, t);
19787     },
19788
19789     /**
19790      * Read-only.  Returns true if the menu is currently displayed, else false.
19791      * @type Boolean
19792      */
19793     isVisible : function(){
19794         return this.el && !this.hidden;
19795     },
19796
19797     /**
19798      * Displays this menu relative to another element
19799      * @param {String/HTMLElement/Roo.Element} element The element to align to
19800      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19801      * the element (defaults to this.defaultAlign)
19802      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19803      */
19804     show : function(el, pos, parentMenu){
19805         this.parentMenu = parentMenu;
19806         if(!this.el){
19807             this.render();
19808         }
19809         this.fireEvent("beforeshow", this);
19810         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19811     },
19812
19813     /**
19814      * Displays this menu at a specific xy position
19815      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19816      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19817      */
19818     showAt : function(xy, parentMenu, /* private: */_e){
19819         this.parentMenu = parentMenu;
19820         if(!this.el){
19821             this.render();
19822         }
19823         if(_e !== false){
19824             this.fireEvent("beforeshow", this);
19825             xy = this.el.adjustForConstraints(xy);
19826         }
19827         this.el.setXY(xy);
19828         this.el.show();
19829         this.hidden = false;
19830         this.focus();
19831         this.fireEvent("show", this);
19832     },
19833
19834     focus : function(){
19835         if(!this.hidden){
19836             this.doFocus.defer(50, this);
19837         }
19838     },
19839
19840     doFocus : function(){
19841         if(!this.hidden){
19842             this.focusEl.focus();
19843         }
19844     },
19845
19846     /**
19847      * Hides this menu and optionally all parent menus
19848      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19849      */
19850     hide : function(deep){
19851         if(this.el && this.isVisible()){
19852             this.fireEvent("beforehide", this);
19853             if(this.activeItem){
19854                 this.activeItem.deactivate();
19855                 this.activeItem = null;
19856             }
19857             this.el.hide();
19858             this.hidden = true;
19859             this.fireEvent("hide", this);
19860         }
19861         if(deep === true && this.parentMenu){
19862             this.parentMenu.hide(true);
19863         }
19864     },
19865
19866     /**
19867      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19868      * Any of the following are valid:
19869      * <ul>
19870      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19871      * <li>An HTMLElement object which will be converted to a menu item</li>
19872      * <li>A menu item config object that will be created as a new menu item</li>
19873      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19874      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19875      * </ul>
19876      * Usage:
19877      * <pre><code>
19878 // Create the menu
19879 var menu = new Roo.menu.Menu();
19880
19881 // Create a menu item to add by reference
19882 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19883
19884 // Add a bunch of items at once using different methods.
19885 // Only the last item added will be returned.
19886 var item = menu.add(
19887     menuItem,                // add existing item by ref
19888     'Dynamic Item',          // new TextItem
19889     '-',                     // new separator
19890     { text: 'Config Item' }  // new item by config
19891 );
19892 </code></pre>
19893      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19894      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19895      */
19896     add : function(){
19897         var a = arguments, l = a.length, item;
19898         for(var i = 0; i < l; i++){
19899             var el = a[i];
19900             if ((typeof(el) == "object") && el.xtype && el.xns) {
19901                 el = Roo.factory(el, Roo.menu);
19902             }
19903             
19904             if(el.render){ // some kind of Item
19905                 item = this.addItem(el);
19906             }else if(typeof el == "string"){ // string
19907                 if(el == "separator" || el == "-"){
19908                     item = this.addSeparator();
19909                 }else{
19910                     item = this.addText(el);
19911                 }
19912             }else if(el.tagName || el.el){ // element
19913                 item = this.addElement(el);
19914             }else if(typeof el == "object"){ // must be menu item config?
19915                 item = this.addMenuItem(el);
19916             }
19917         }
19918         return item;
19919     },
19920
19921     /**
19922      * Returns this menu's underlying {@link Roo.Element} object
19923      * @return {Roo.Element} The element
19924      */
19925     getEl : function(){
19926         if(!this.el){
19927             this.render();
19928         }
19929         return this.el;
19930     },
19931
19932     /**
19933      * Adds a separator bar to the menu
19934      * @return {Roo.menu.Item} The menu item that was added
19935      */
19936     addSeparator : function(){
19937         return this.addItem(new Roo.menu.Separator());
19938     },
19939
19940     /**
19941      * Adds an {@link Roo.Element} object to the menu
19942      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19943      * @return {Roo.menu.Item} The menu item that was added
19944      */
19945     addElement : function(el){
19946         return this.addItem(new Roo.menu.BaseItem(el));
19947     },
19948
19949     /**
19950      * Adds an existing object based on {@link Roo.menu.Item} to the menu
19951      * @param {Roo.menu.Item} item The menu item to add
19952      * @return {Roo.menu.Item} The menu item that was added
19953      */
19954     addItem : function(item){
19955         this.items.add(item);
19956         if(this.ul){
19957             var li = document.createElement("li");
19958             li.className = "x-menu-list-item";
19959             this.ul.dom.appendChild(li);
19960             item.render(li, this);
19961             this.delayAutoWidth();
19962         }
19963         return item;
19964     },
19965
19966     /**
19967      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
19968      * @param {Object} config A MenuItem config object
19969      * @return {Roo.menu.Item} The menu item that was added
19970      */
19971     addMenuItem : function(config){
19972         if(!(config instanceof Roo.menu.Item)){
19973             if(typeof config.checked == "boolean"){ // must be check menu item config?
19974                 config = new Roo.menu.CheckItem(config);
19975             }else{
19976                 config = new Roo.menu.Item(config);
19977             }
19978         }
19979         return this.addItem(config);
19980     },
19981
19982     /**
19983      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
19984      * @param {String} text The text to display in the menu item
19985      * @return {Roo.menu.Item} The menu item that was added
19986      */
19987     addText : function(text){
19988         return this.addItem(new Roo.menu.TextItem({ text : text }));
19989     },
19990
19991     /**
19992      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
19993      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
19994      * @param {Roo.menu.Item} item The menu item to add
19995      * @return {Roo.menu.Item} The menu item that was added
19996      */
19997     insert : function(index, item){
19998         this.items.insert(index, item);
19999         if(this.ul){
20000             var li = document.createElement("li");
20001             li.className = "x-menu-list-item";
20002             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
20003             item.render(li, this);
20004             this.delayAutoWidth();
20005         }
20006         return item;
20007     },
20008
20009     /**
20010      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
20011      * @param {Roo.menu.Item} item The menu item to remove
20012      */
20013     remove : function(item){
20014         this.items.removeKey(item.id);
20015         item.destroy();
20016     },
20017
20018     /**
20019      * Removes and destroys all items in the menu
20020      */
20021     removeAll : function(){
20022         var f;
20023         while(f = this.items.first()){
20024             this.remove(f);
20025         }
20026     }
20027 });
20028
20029 // MenuNav is a private utility class used internally by the Menu
20030 Roo.menu.MenuNav = function(menu){
20031     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
20032     this.scope = this.menu = menu;
20033 };
20034
20035 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
20036     doRelay : function(e, h){
20037         var k = e.getKey();
20038         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
20039             this.menu.tryActivate(0, 1);
20040             return false;
20041         }
20042         return h.call(this.scope || this, e, this.menu);
20043     },
20044
20045     up : function(e, m){
20046         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
20047             m.tryActivate(m.items.length-1, -1);
20048         }
20049     },
20050
20051     down : function(e, m){
20052         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
20053             m.tryActivate(0, 1);
20054         }
20055     },
20056
20057     right : function(e, m){
20058         if(m.activeItem){
20059             m.activeItem.expandMenu(true);
20060         }
20061     },
20062
20063     left : function(e, m){
20064         m.hide();
20065         if(m.parentMenu && m.parentMenu.activeItem){
20066             m.parentMenu.activeItem.activate();
20067         }
20068     },
20069
20070     enter : function(e, m){
20071         if(m.activeItem){
20072             e.stopPropagation();
20073             m.activeItem.onClick(e);
20074             m.fireEvent("click", this, m.activeItem);
20075             return true;
20076         }
20077     }
20078 });/*
20079  * Based on:
20080  * Ext JS Library 1.1.1
20081  * Copyright(c) 2006-2007, Ext JS, LLC.
20082  *
20083  * Originally Released Under LGPL - original licence link has changed is not relivant.
20084  *
20085  * Fork - LGPL
20086  * <script type="text/javascript">
20087  */
20088  
20089 /**
20090  * @class Roo.menu.MenuMgr
20091  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
20092  * @singleton
20093  */
20094 Roo.menu.MenuMgr = function(){
20095    var menus, active, groups = {}, attached = false, lastShow = new Date();
20096
20097    // private - called when first menu is created
20098    function init(){
20099        menus = {};
20100        active = new Roo.util.MixedCollection();
20101        Roo.get(document).addKeyListener(27, function(){
20102            if(active.length > 0){
20103                hideAll();
20104            }
20105        });
20106    }
20107
20108    // private
20109    function hideAll(){
20110        if(active && active.length > 0){
20111            var c = active.clone();
20112            c.each(function(m){
20113                m.hide();
20114            });
20115        }
20116    }
20117
20118    // private
20119    function onHide(m){
20120        active.remove(m);
20121        if(active.length < 1){
20122            Roo.get(document).un("mousedown", onMouseDown);
20123            attached = false;
20124        }
20125    }
20126
20127    // private
20128    function onShow(m){
20129        var last = active.last();
20130        lastShow = new Date();
20131        active.add(m);
20132        if(!attached){
20133            Roo.get(document).on("mousedown", onMouseDown);
20134            attached = true;
20135        }
20136        if(m.parentMenu){
20137           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
20138           m.parentMenu.activeChild = m;
20139        }else if(last && last.isVisible()){
20140           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
20141        }
20142    }
20143
20144    // private
20145    function onBeforeHide(m){
20146        if(m.activeChild){
20147            m.activeChild.hide();
20148        }
20149        if(m.autoHideTimer){
20150            clearTimeout(m.autoHideTimer);
20151            delete m.autoHideTimer;
20152        }
20153    }
20154
20155    // private
20156    function onBeforeShow(m){
20157        var pm = m.parentMenu;
20158        if(!pm && !m.allowOtherMenus){
20159            hideAll();
20160        }else if(pm && pm.activeChild && active != m){
20161            pm.activeChild.hide();
20162        }
20163    }
20164
20165    // private
20166    function onMouseDown(e){
20167        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
20168            hideAll();
20169        }
20170    }
20171
20172    // private
20173    function onBeforeCheck(mi, state){
20174        if(state){
20175            var g = groups[mi.group];
20176            for(var i = 0, l = g.length; i < l; i++){
20177                if(g[i] != mi){
20178                    g[i].setChecked(false);
20179                }
20180            }
20181        }
20182    }
20183
20184    return {
20185
20186        /**
20187         * Hides all menus that are currently visible
20188         */
20189        hideAll : function(){
20190             hideAll();  
20191        },
20192
20193        // private
20194        register : function(menu){
20195            if(!menus){
20196                init();
20197            }
20198            menus[menu.id] = menu;
20199            menu.on("beforehide", onBeforeHide);
20200            menu.on("hide", onHide);
20201            menu.on("beforeshow", onBeforeShow);
20202            menu.on("show", onShow);
20203            var g = menu.group;
20204            if(g && menu.events["checkchange"]){
20205                if(!groups[g]){
20206                    groups[g] = [];
20207                }
20208                groups[g].push(menu);
20209                menu.on("checkchange", onCheck);
20210            }
20211        },
20212
20213         /**
20214          * Returns a {@link Roo.menu.Menu} object
20215          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
20216          * be used to generate and return a new Menu instance.
20217          */
20218        get : function(menu){
20219            if(typeof menu == "string"){ // menu id
20220                return menus[menu];
20221            }else if(menu.events){  // menu instance
20222                return menu;
20223            }else if(typeof menu.length == 'number'){ // array of menu items?
20224                return new Roo.menu.Menu({items:menu});
20225            }else{ // otherwise, must be a config
20226                return new Roo.menu.Menu(menu);
20227            }
20228        },
20229
20230        // private
20231        unregister : function(menu){
20232            delete menus[menu.id];
20233            menu.un("beforehide", onBeforeHide);
20234            menu.un("hide", onHide);
20235            menu.un("beforeshow", onBeforeShow);
20236            menu.un("show", onShow);
20237            var g = menu.group;
20238            if(g && menu.events["checkchange"]){
20239                groups[g].remove(menu);
20240                menu.un("checkchange", onCheck);
20241            }
20242        },
20243
20244        // private
20245        registerCheckable : function(menuItem){
20246            var g = menuItem.group;
20247            if(g){
20248                if(!groups[g]){
20249                    groups[g] = [];
20250                }
20251                groups[g].push(menuItem);
20252                menuItem.on("beforecheckchange", onBeforeCheck);
20253            }
20254        },
20255
20256        // private
20257        unregisterCheckable : function(menuItem){
20258            var g = menuItem.group;
20259            if(g){
20260                groups[g].remove(menuItem);
20261                menuItem.un("beforecheckchange", onBeforeCheck);
20262            }
20263        }
20264    };
20265 }();/*
20266  * Based on:
20267  * Ext JS Library 1.1.1
20268  * Copyright(c) 2006-2007, Ext JS, LLC.
20269  *
20270  * Originally Released Under LGPL - original licence link has changed is not relivant.
20271  *
20272  * Fork - LGPL
20273  * <script type="text/javascript">
20274  */
20275  
20276
20277 /**
20278  * @class Roo.menu.BaseItem
20279  * @extends Roo.Component
20280  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
20281  * management and base configuration options shared by all menu components.
20282  * @constructor
20283  * Creates a new BaseItem
20284  * @param {Object} config Configuration options
20285  */
20286 Roo.menu.BaseItem = function(config){
20287     Roo.menu.BaseItem.superclass.constructor.call(this, config);
20288
20289     this.addEvents({
20290         /**
20291          * @event click
20292          * Fires when this item is clicked
20293          * @param {Roo.menu.BaseItem} this
20294          * @param {Roo.EventObject} e
20295          */
20296         click: true,
20297         /**
20298          * @event activate
20299          * Fires when this item is activated
20300          * @param {Roo.menu.BaseItem} this
20301          */
20302         activate : true,
20303         /**
20304          * @event deactivate
20305          * Fires when this item is deactivated
20306          * @param {Roo.menu.BaseItem} this
20307          */
20308         deactivate : true
20309     });
20310
20311     if(this.handler){
20312         this.on("click", this.handler, this.scope, true);
20313     }
20314 };
20315
20316 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20317     /**
20318      * @cfg {Function} handler
20319      * A function that will handle the click event of this menu item (defaults to undefined)
20320      */
20321     /**
20322      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20323      */
20324     canActivate : false,
20325     /**
20326      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20327      */
20328     activeClass : "x-menu-item-active",
20329     /**
20330      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20331      */
20332     hideOnClick : true,
20333     /**
20334      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20335      */
20336     hideDelay : 100,
20337
20338     // private
20339     ctype: "Roo.menu.BaseItem",
20340
20341     // private
20342     actionMode : "container",
20343
20344     // private
20345     render : function(container, parentMenu){
20346         this.parentMenu = parentMenu;
20347         Roo.menu.BaseItem.superclass.render.call(this, container);
20348         this.container.menuItemId = this.id;
20349     },
20350
20351     // private
20352     onRender : function(container, position){
20353         this.el = Roo.get(this.el);
20354         container.dom.appendChild(this.el.dom);
20355     },
20356
20357     // private
20358     onClick : function(e){
20359         if(!this.disabled && this.fireEvent("click", this, e) !== false
20360                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20361             this.handleClick(e);
20362         }else{
20363             e.stopEvent();
20364         }
20365     },
20366
20367     // private
20368     activate : function(){
20369         if(this.disabled){
20370             return false;
20371         }
20372         var li = this.container;
20373         li.addClass(this.activeClass);
20374         this.region = li.getRegion().adjust(2, 2, -2, -2);
20375         this.fireEvent("activate", this);
20376         return true;
20377     },
20378
20379     // private
20380     deactivate : function(){
20381         this.container.removeClass(this.activeClass);
20382         this.fireEvent("deactivate", this);
20383     },
20384
20385     // private
20386     shouldDeactivate : function(e){
20387         return !this.region || !this.region.contains(e.getPoint());
20388     },
20389
20390     // private
20391     handleClick : function(e){
20392         if(this.hideOnClick){
20393             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20394         }
20395     },
20396
20397     // private
20398     expandMenu : function(autoActivate){
20399         // do nothing
20400     },
20401
20402     // private
20403     hideMenu : function(){
20404         // do nothing
20405     }
20406 });/*
20407  * Based on:
20408  * Ext JS Library 1.1.1
20409  * Copyright(c) 2006-2007, Ext JS, LLC.
20410  *
20411  * Originally Released Under LGPL - original licence link has changed is not relivant.
20412  *
20413  * Fork - LGPL
20414  * <script type="text/javascript">
20415  */
20416  
20417 /**
20418  * @class Roo.menu.Adapter
20419  * @extends Roo.menu.BaseItem
20420  * 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.
20421  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20422  * @constructor
20423  * Creates a new Adapter
20424  * @param {Object} config Configuration options
20425  */
20426 Roo.menu.Adapter = function(component, config){
20427     Roo.menu.Adapter.superclass.constructor.call(this, config);
20428     this.component = component;
20429 };
20430 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20431     // private
20432     canActivate : true,
20433
20434     // private
20435     onRender : function(container, position){
20436         this.component.render(container);
20437         this.el = this.component.getEl();
20438     },
20439
20440     // private
20441     activate : function(){
20442         if(this.disabled){
20443             return false;
20444         }
20445         this.component.focus();
20446         this.fireEvent("activate", this);
20447         return true;
20448     },
20449
20450     // private
20451     deactivate : function(){
20452         this.fireEvent("deactivate", this);
20453     },
20454
20455     // private
20456     disable : function(){
20457         this.component.disable();
20458         Roo.menu.Adapter.superclass.disable.call(this);
20459     },
20460
20461     // private
20462     enable : function(){
20463         this.component.enable();
20464         Roo.menu.Adapter.superclass.enable.call(this);
20465     }
20466 });/*
20467  * Based on:
20468  * Ext JS Library 1.1.1
20469  * Copyright(c) 2006-2007, Ext JS, LLC.
20470  *
20471  * Originally Released Under LGPL - original licence link has changed is not relivant.
20472  *
20473  * Fork - LGPL
20474  * <script type="text/javascript">
20475  */
20476
20477 /**
20478  * @class Roo.menu.TextItem
20479  * @extends Roo.menu.BaseItem
20480  * Adds a static text string to a menu, usually used as either a heading or group separator.
20481  * Note: old style constructor with text is still supported.
20482  * 
20483  * @constructor
20484  * Creates a new TextItem
20485  * @param {Object} cfg Configuration
20486  */
20487 Roo.menu.TextItem = function(cfg){
20488     if (typeof(cfg) == 'string') {
20489         this.text = cfg;
20490     } else {
20491         Roo.apply(this,cfg);
20492     }
20493     
20494     Roo.menu.TextItem.superclass.constructor.call(this);
20495 };
20496
20497 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20498     /**
20499      * @cfg {Boolean} text Text to show on item.
20500      */
20501     text : '',
20502     
20503     /**
20504      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20505      */
20506     hideOnClick : false,
20507     /**
20508      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20509      */
20510     itemCls : "x-menu-text",
20511
20512     // private
20513     onRender : function(){
20514         var s = document.createElement("span");
20515         s.className = this.itemCls;
20516         s.innerHTML = this.text;
20517         this.el = s;
20518         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20519     }
20520 });/*
20521  * Based on:
20522  * Ext JS Library 1.1.1
20523  * Copyright(c) 2006-2007, Ext JS, LLC.
20524  *
20525  * Originally Released Under LGPL - original licence link has changed is not relivant.
20526  *
20527  * Fork - LGPL
20528  * <script type="text/javascript">
20529  */
20530
20531 /**
20532  * @class Roo.menu.Separator
20533  * @extends Roo.menu.BaseItem
20534  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20535  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20536  * @constructor
20537  * @param {Object} config Configuration options
20538  */
20539 Roo.menu.Separator = function(config){
20540     Roo.menu.Separator.superclass.constructor.call(this, config);
20541 };
20542
20543 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20544     /**
20545      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20546      */
20547     itemCls : "x-menu-sep",
20548     /**
20549      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20550      */
20551     hideOnClick : false,
20552
20553     // private
20554     onRender : function(li){
20555         var s = document.createElement("span");
20556         s.className = this.itemCls;
20557         s.innerHTML = "&#160;";
20558         this.el = s;
20559         li.addClass("x-menu-sep-li");
20560         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20561     }
20562 });/*
20563  * Based on:
20564  * Ext JS Library 1.1.1
20565  * Copyright(c) 2006-2007, Ext JS, LLC.
20566  *
20567  * Originally Released Under LGPL - original licence link has changed is not relivant.
20568  *
20569  * Fork - LGPL
20570  * <script type="text/javascript">
20571  */
20572 /**
20573  * @class Roo.menu.Item
20574  * @extends Roo.menu.BaseItem
20575  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20576  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20577  * activation and click handling.
20578  * @constructor
20579  * Creates a new Item
20580  * @param {Object} config Configuration options
20581  */
20582 Roo.menu.Item = function(config){
20583     Roo.menu.Item.superclass.constructor.call(this, config);
20584     if(this.menu){
20585         this.menu = Roo.menu.MenuMgr.get(this.menu);
20586     }
20587 };
20588 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20589     
20590     /**
20591      * @cfg {String} text
20592      * The text to show on the menu item.
20593      */
20594     text: '',
20595      /**
20596      * @cfg {String} HTML to render in menu
20597      * The text to show on the menu item (HTML version).
20598      */
20599     html: '',
20600     /**
20601      * @cfg {String} icon
20602      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20603      */
20604     icon: undefined,
20605     /**
20606      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20607      */
20608     itemCls : "x-menu-item",
20609     /**
20610      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20611      */
20612     canActivate : true,
20613     /**
20614      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20615      */
20616     showDelay: 200,
20617     // doc'd in BaseItem
20618     hideDelay: 200,
20619
20620     // private
20621     ctype: "Roo.menu.Item",
20622     
20623     // private
20624     onRender : function(container, position){
20625         var el = document.createElement("a");
20626         el.hideFocus = true;
20627         el.unselectable = "on";
20628         el.href = this.href || "#";
20629         if(this.hrefTarget){
20630             el.target = this.hrefTarget;
20631         }
20632         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20633         
20634         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20635         
20636         el.innerHTML = String.format(
20637                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20638                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20639         this.el = el;
20640         Roo.menu.Item.superclass.onRender.call(this, container, position);
20641     },
20642
20643     /**
20644      * Sets the text to display in this menu item
20645      * @param {String} text The text to display
20646      * @param {Boolean} isHTML true to indicate text is pure html.
20647      */
20648     setText : function(text, isHTML){
20649         if (isHTML) {
20650             this.html = text;
20651         } else {
20652             this.text = text;
20653             this.html = '';
20654         }
20655         if(this.rendered){
20656             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20657      
20658             this.el.update(String.format(
20659                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20660                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20661             this.parentMenu.autoWidth();
20662         }
20663     },
20664
20665     // private
20666     handleClick : function(e){
20667         if(!this.href){ // if no link defined, stop the event automatically
20668             e.stopEvent();
20669         }
20670         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20671     },
20672
20673     // private
20674     activate : function(autoExpand){
20675         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20676             this.focus();
20677             if(autoExpand){
20678                 this.expandMenu();
20679             }
20680         }
20681         return true;
20682     },
20683
20684     // private
20685     shouldDeactivate : function(e){
20686         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20687             if(this.menu && this.menu.isVisible()){
20688                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20689             }
20690             return true;
20691         }
20692         return false;
20693     },
20694
20695     // private
20696     deactivate : function(){
20697         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20698         this.hideMenu();
20699     },
20700
20701     // private
20702     expandMenu : function(autoActivate){
20703         if(!this.disabled && this.menu){
20704             clearTimeout(this.hideTimer);
20705             delete this.hideTimer;
20706             if(!this.menu.isVisible() && !this.showTimer){
20707                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20708             }else if (this.menu.isVisible() && autoActivate){
20709                 this.menu.tryActivate(0, 1);
20710             }
20711         }
20712     },
20713
20714     // private
20715     deferExpand : function(autoActivate){
20716         delete this.showTimer;
20717         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20718         if(autoActivate){
20719             this.menu.tryActivate(0, 1);
20720         }
20721     },
20722
20723     // private
20724     hideMenu : function(){
20725         clearTimeout(this.showTimer);
20726         delete this.showTimer;
20727         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20728             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20729         }
20730     },
20731
20732     // private
20733     deferHide : function(){
20734         delete this.hideTimer;
20735         this.menu.hide();
20736     }
20737 });/*
20738  * Based on:
20739  * Ext JS Library 1.1.1
20740  * Copyright(c) 2006-2007, Ext JS, LLC.
20741  *
20742  * Originally Released Under LGPL - original licence link has changed is not relivant.
20743  *
20744  * Fork - LGPL
20745  * <script type="text/javascript">
20746  */
20747  
20748 /**
20749  * @class Roo.menu.CheckItem
20750  * @extends Roo.menu.Item
20751  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20752  * @constructor
20753  * Creates a new CheckItem
20754  * @param {Object} config Configuration options
20755  */
20756 Roo.menu.CheckItem = function(config){
20757     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20758     this.addEvents({
20759         /**
20760          * @event beforecheckchange
20761          * Fires before the checked value is set, providing an opportunity to cancel if needed
20762          * @param {Roo.menu.CheckItem} this
20763          * @param {Boolean} checked The new checked value that will be set
20764          */
20765         "beforecheckchange" : true,
20766         /**
20767          * @event checkchange
20768          * Fires after the checked value has been set
20769          * @param {Roo.menu.CheckItem} this
20770          * @param {Boolean} checked The checked value that was set
20771          */
20772         "checkchange" : true
20773     });
20774     if(this.checkHandler){
20775         this.on('checkchange', this.checkHandler, this.scope);
20776     }
20777 };
20778 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20779     /**
20780      * @cfg {String} group
20781      * All check items with the same group name will automatically be grouped into a single-select
20782      * radio button group (defaults to '')
20783      */
20784     /**
20785      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20786      */
20787     itemCls : "x-menu-item x-menu-check-item",
20788     /**
20789      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20790      */
20791     groupClass : "x-menu-group-item",
20792
20793     /**
20794      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20795      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20796      * initialized with checked = true will be rendered as checked.
20797      */
20798     checked: false,
20799
20800     // private
20801     ctype: "Roo.menu.CheckItem",
20802
20803     // private
20804     onRender : function(c){
20805         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20806         if(this.group){
20807             this.el.addClass(this.groupClass);
20808         }
20809         Roo.menu.MenuMgr.registerCheckable(this);
20810         if(this.checked){
20811             this.checked = false;
20812             this.setChecked(true, true);
20813         }
20814     },
20815
20816     // private
20817     destroy : function(){
20818         if(this.rendered){
20819             Roo.menu.MenuMgr.unregisterCheckable(this);
20820         }
20821         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20822     },
20823
20824     /**
20825      * Set the checked state of this item
20826      * @param {Boolean} checked The new checked value
20827      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20828      */
20829     setChecked : function(state, suppressEvent){
20830         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20831             if(this.container){
20832                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20833             }
20834             this.checked = state;
20835             if(suppressEvent !== true){
20836                 this.fireEvent("checkchange", this, state);
20837             }
20838         }
20839     },
20840
20841     // private
20842     handleClick : function(e){
20843        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20844            this.setChecked(!this.checked);
20845        }
20846        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20847     }
20848 });/*
20849  * Based on:
20850  * Ext JS Library 1.1.1
20851  * Copyright(c) 2006-2007, Ext JS, LLC.
20852  *
20853  * Originally Released Under LGPL - original licence link has changed is not relivant.
20854  *
20855  * Fork - LGPL
20856  * <script type="text/javascript">
20857  */
20858  
20859 /**
20860  * @class Roo.menu.DateItem
20861  * @extends Roo.menu.Adapter
20862  * A menu item that wraps the {@link Roo.DatPicker} component.
20863  * @constructor
20864  * Creates a new DateItem
20865  * @param {Object} config Configuration options
20866  */
20867 Roo.menu.DateItem = function(config){
20868     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20869     /** The Roo.DatePicker object @type Roo.DatePicker */
20870     this.picker = this.component;
20871     this.addEvents({select: true});
20872     
20873     this.picker.on("render", function(picker){
20874         picker.getEl().swallowEvent("click");
20875         picker.container.addClass("x-menu-date-item");
20876     });
20877
20878     this.picker.on("select", this.onSelect, this);
20879 };
20880
20881 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20882     // private
20883     onSelect : function(picker, date){
20884         this.fireEvent("select", this, date, picker);
20885         Roo.menu.DateItem.superclass.handleClick.call(this);
20886     }
20887 });/*
20888  * Based on:
20889  * Ext JS Library 1.1.1
20890  * Copyright(c) 2006-2007, Ext JS, LLC.
20891  *
20892  * Originally Released Under LGPL - original licence link has changed is not relivant.
20893  *
20894  * Fork - LGPL
20895  * <script type="text/javascript">
20896  */
20897  
20898 /**
20899  * @class Roo.menu.ColorItem
20900  * @extends Roo.menu.Adapter
20901  * A menu item that wraps the {@link Roo.ColorPalette} component.
20902  * @constructor
20903  * Creates a new ColorItem
20904  * @param {Object} config Configuration options
20905  */
20906 Roo.menu.ColorItem = function(config){
20907     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20908     /** The Roo.ColorPalette object @type Roo.ColorPalette */
20909     this.palette = this.component;
20910     this.relayEvents(this.palette, ["select"]);
20911     if(this.selectHandler){
20912         this.on('select', this.selectHandler, this.scope);
20913     }
20914 };
20915 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
20916  * Based on:
20917  * Ext JS Library 1.1.1
20918  * Copyright(c) 2006-2007, Ext JS, LLC.
20919  *
20920  * Originally Released Under LGPL - original licence link has changed is not relivant.
20921  *
20922  * Fork - LGPL
20923  * <script type="text/javascript">
20924  */
20925  
20926
20927 /**
20928  * @class Roo.menu.DateMenu
20929  * @extends Roo.menu.Menu
20930  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20931  * @constructor
20932  * Creates a new DateMenu
20933  * @param {Object} config Configuration options
20934  */
20935 Roo.menu.DateMenu = function(config){
20936     Roo.menu.DateMenu.superclass.constructor.call(this, config);
20937     this.plain = true;
20938     var di = new Roo.menu.DateItem(config);
20939     this.add(di);
20940     /**
20941      * The {@link Roo.DatePicker} instance for this DateMenu
20942      * @type DatePicker
20943      */
20944     this.picker = di.picker;
20945     /**
20946      * @event select
20947      * @param {DatePicker} picker
20948      * @param {Date} date
20949      */
20950     this.relayEvents(di, ["select"]);
20951
20952     this.on('beforeshow', function(){
20953         if(this.picker){
20954             this.picker.hideMonthPicker(true);
20955         }
20956     }, this);
20957 };
20958 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
20959     cls:'x-date-menu'
20960 });/*
20961  * Based on:
20962  * Ext JS Library 1.1.1
20963  * Copyright(c) 2006-2007, Ext JS, LLC.
20964  *
20965  * Originally Released Under LGPL - original licence link has changed is not relivant.
20966  *
20967  * Fork - LGPL
20968  * <script type="text/javascript">
20969  */
20970  
20971
20972 /**
20973  * @class Roo.menu.ColorMenu
20974  * @extends Roo.menu.Menu
20975  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
20976  * @constructor
20977  * Creates a new ColorMenu
20978  * @param {Object} config Configuration options
20979  */
20980 Roo.menu.ColorMenu = function(config){
20981     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
20982     this.plain = true;
20983     var ci = new Roo.menu.ColorItem(config);
20984     this.add(ci);
20985     /**
20986      * The {@link Roo.ColorPalette} instance for this ColorMenu
20987      * @type ColorPalette
20988      */
20989     this.palette = ci.palette;
20990     /**
20991      * @event select
20992      * @param {ColorPalette} palette
20993      * @param {String} color
20994      */
20995     this.relayEvents(ci, ["select"]);
20996 };
20997 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
20998  * Based on:
20999  * Ext JS Library 1.1.1
21000  * Copyright(c) 2006-2007, Ext JS, LLC.
21001  *
21002  * Originally Released Under LGPL - original licence link has changed is not relivant.
21003  *
21004  * Fork - LGPL
21005  * <script type="text/javascript">
21006  */
21007  
21008 /**
21009  * @class Roo.form.Field
21010  * @extends Roo.BoxComponent
21011  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
21012  * @constructor
21013  * Creates a new Field
21014  * @param {Object} config Configuration options
21015  */
21016 Roo.form.Field = function(config){
21017     Roo.form.Field.superclass.constructor.call(this, config);
21018 };
21019
21020 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
21021     /**
21022      * @cfg {String} fieldLabel Label to use when rendering a form.
21023      */
21024        /**
21025      * @cfg {String} qtip Mouse over tip
21026      */
21027      
21028     /**
21029      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
21030      */
21031     invalidClass : "x-form-invalid",
21032     /**
21033      * @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")
21034      */
21035     invalidText : "The value in this field is invalid",
21036     /**
21037      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
21038      */
21039     focusClass : "x-form-focus",
21040     /**
21041      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
21042       automatic validation (defaults to "keyup").
21043      */
21044     validationEvent : "keyup",
21045     /**
21046      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
21047      */
21048     validateOnBlur : true,
21049     /**
21050      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
21051      */
21052     validationDelay : 250,
21053     /**
21054      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21055      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
21056      */
21057     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
21058     /**
21059      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
21060      */
21061     fieldClass : "x-form-field",
21062     /**
21063      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
21064      *<pre>
21065 Value         Description
21066 -----------   ----------------------------------------------------------------------
21067 qtip          Display a quick tip when the user hovers over the field
21068 title         Display a default browser title attribute popup
21069 under         Add a block div beneath the field containing the error text
21070 side          Add an error icon to the right of the field with a popup on hover
21071 [element id]  Add the error text directly to the innerHTML of the specified element
21072 </pre>
21073      */
21074     msgTarget : 'qtip',
21075     /**
21076      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
21077      */
21078     msgFx : 'normal',
21079
21080     /**
21081      * @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.
21082      */
21083     readOnly : false,
21084
21085     /**
21086      * @cfg {Boolean} disabled True to disable the field (defaults to false).
21087      */
21088     disabled : false,
21089
21090     /**
21091      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
21092      */
21093     inputType : undefined,
21094     
21095     /**
21096      * @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).
21097          */
21098         tabIndex : undefined,
21099         
21100     // private
21101     isFormField : true,
21102
21103     // private
21104     hasFocus : false,
21105     /**
21106      * @property {Roo.Element} fieldEl
21107      * Element Containing the rendered Field (with label etc.)
21108      */
21109     /**
21110      * @cfg {Mixed} value A value to initialize this field with.
21111      */
21112     value : undefined,
21113
21114     /**
21115      * @cfg {String} name The field's HTML name attribute.
21116      */
21117     /**
21118      * @cfg {String} cls A CSS class to apply to the field's underlying element.
21119      */
21120
21121         // private ??
21122         initComponent : function(){
21123         Roo.form.Field.superclass.initComponent.call(this);
21124         this.addEvents({
21125             /**
21126              * @event focus
21127              * Fires when this field receives input focus.
21128              * @param {Roo.form.Field} this
21129              */
21130             focus : true,
21131             /**
21132              * @event blur
21133              * Fires when this field loses input focus.
21134              * @param {Roo.form.Field} this
21135              */
21136             blur : true,
21137             /**
21138              * @event specialkey
21139              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
21140              * {@link Roo.EventObject#getKey} to determine which key was pressed.
21141              * @param {Roo.form.Field} this
21142              * @param {Roo.EventObject} e The event object
21143              */
21144             specialkey : true,
21145             /**
21146              * @event change
21147              * Fires just before the field blurs if the field value has changed.
21148              * @param {Roo.form.Field} this
21149              * @param {Mixed} newValue The new value
21150              * @param {Mixed} oldValue The original value
21151              */
21152             change : true,
21153             /**
21154              * @event invalid
21155              * Fires after the field has been marked as invalid.
21156              * @param {Roo.form.Field} this
21157              * @param {String} msg The validation message
21158              */
21159             invalid : true,
21160             /**
21161              * @event valid
21162              * Fires after the field has been validated with no errors.
21163              * @param {Roo.form.Field} this
21164              */
21165             valid : true,
21166              /**
21167              * @event keyup
21168              * Fires after the key up
21169              * @param {Roo.form.Field} this
21170              * @param {Roo.EventObject}  e The event Object
21171              */
21172             keyup : true
21173         });
21174     },
21175
21176     /**
21177      * Returns the name attribute of the field if available
21178      * @return {String} name The field name
21179      */
21180     getName: function(){
21181          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
21182     },
21183
21184     // private
21185     onRender : function(ct, position){
21186         Roo.form.Field.superclass.onRender.call(this, ct, position);
21187         if(!this.el){
21188             var cfg = this.getAutoCreate();
21189             if(!cfg.name){
21190                 cfg.name = this.name || this.id;
21191             }
21192             if(this.inputType){
21193                 cfg.type = this.inputType;
21194             }
21195             this.el = ct.createChild(cfg, position);
21196         }
21197         var type = this.el.dom.type;
21198         if(type){
21199             if(type == 'password'){
21200                 type = 'text';
21201             }
21202             this.el.addClass('x-form-'+type);
21203         }
21204         if(this.readOnly){
21205             this.el.dom.readOnly = true;
21206         }
21207         if(this.tabIndex !== undefined){
21208             this.el.dom.setAttribute('tabIndex', this.tabIndex);
21209         }
21210
21211         this.el.addClass([this.fieldClass, this.cls]);
21212         this.initValue();
21213     },
21214
21215     /**
21216      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
21217      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
21218      * @return {Roo.form.Field} this
21219      */
21220     applyTo : function(target){
21221         this.allowDomMove = false;
21222         this.el = Roo.get(target);
21223         this.render(this.el.dom.parentNode);
21224         return this;
21225     },
21226
21227     // private
21228     initValue : function(){
21229         if(this.value !== undefined){
21230             this.setValue(this.value);
21231         }else if(this.el.dom.value.length > 0){
21232             this.setValue(this.el.dom.value);
21233         }
21234     },
21235
21236     /**
21237      * Returns true if this field has been changed since it was originally loaded and is not disabled.
21238      */
21239     isDirty : function() {
21240         if(this.disabled) {
21241             return false;
21242         }
21243         return String(this.getValue()) !== String(this.originalValue);
21244     },
21245
21246     // private
21247     afterRender : function(){
21248         Roo.form.Field.superclass.afterRender.call(this);
21249         this.initEvents();
21250     },
21251
21252     // private
21253     fireKey : function(e){
21254         //Roo.log('field ' + e.getKey());
21255         if(e.isNavKeyPress()){
21256             this.fireEvent("specialkey", this, e);
21257         }
21258     },
21259
21260     /**
21261      * Resets the current field value to the originally loaded value and clears any validation messages
21262      */
21263     reset : function(){
21264         this.setValue(this.originalValue);
21265         this.clearInvalid();
21266     },
21267
21268     // private
21269     initEvents : function(){
21270         // safari killled keypress - so keydown is now used..
21271         this.el.on("keydown" , this.fireKey,  this);
21272         this.el.on("focus", this.onFocus,  this);
21273         this.el.on("blur", this.onBlur,  this);
21274         this.el.relayEvent('keyup', this);
21275
21276         // reference to original value for reset
21277         this.originalValue = this.getValue();
21278     },
21279
21280     // private
21281     onFocus : function(){
21282         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21283             this.el.addClass(this.focusClass);
21284         }
21285         if(!this.hasFocus){
21286             this.hasFocus = true;
21287             this.startValue = this.getValue();
21288             this.fireEvent("focus", this);
21289         }
21290     },
21291
21292     beforeBlur : Roo.emptyFn,
21293
21294     // private
21295     onBlur : function(){
21296         this.beforeBlur();
21297         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21298             this.el.removeClass(this.focusClass);
21299         }
21300         this.hasFocus = false;
21301         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21302             this.validate();
21303         }
21304         var v = this.getValue();
21305         if(String(v) !== String(this.startValue)){
21306             this.fireEvent('change', this, v, this.startValue);
21307         }
21308         this.fireEvent("blur", this);
21309     },
21310
21311     /**
21312      * Returns whether or not the field value is currently valid
21313      * @param {Boolean} preventMark True to disable marking the field invalid
21314      * @return {Boolean} True if the value is valid, else false
21315      */
21316     isValid : function(preventMark){
21317         if(this.disabled){
21318             return true;
21319         }
21320         var restore = this.preventMark;
21321         this.preventMark = preventMark === true;
21322         var v = this.validateValue(this.processValue(this.getRawValue()));
21323         this.preventMark = restore;
21324         return v;
21325     },
21326
21327     /**
21328      * Validates the field value
21329      * @return {Boolean} True if the value is valid, else false
21330      */
21331     validate : function(){
21332         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21333             this.clearInvalid();
21334             return true;
21335         }
21336         return false;
21337     },
21338
21339     processValue : function(value){
21340         return value;
21341     },
21342
21343     // private
21344     // Subclasses should provide the validation implementation by overriding this
21345     validateValue : function(value){
21346         return true;
21347     },
21348
21349     /**
21350      * Mark this field as invalid
21351      * @param {String} msg The validation message
21352      */
21353     markInvalid : function(msg){
21354         if(!this.rendered || this.preventMark){ // not rendered
21355             return;
21356         }
21357         this.el.addClass(this.invalidClass);
21358         msg = msg || this.invalidText;
21359         switch(this.msgTarget){
21360             case 'qtip':
21361                 this.el.dom.qtip = msg;
21362                 this.el.dom.qclass = 'x-form-invalid-tip';
21363                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21364                     Roo.QuickTips.enable();
21365                 }
21366                 break;
21367             case 'title':
21368                 this.el.dom.title = msg;
21369                 break;
21370             case 'under':
21371                 if(!this.errorEl){
21372                     var elp = this.el.findParent('.x-form-element', 5, true);
21373                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21374                     this.errorEl.setWidth(elp.getWidth(true)-20);
21375                 }
21376                 this.errorEl.update(msg);
21377                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21378                 break;
21379             case 'side':
21380                 if(!this.errorIcon){
21381                     var elp = this.el.findParent('.x-form-element', 5, true);
21382                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21383                 }
21384                 this.alignErrorIcon();
21385                 this.errorIcon.dom.qtip = msg;
21386                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21387                 this.errorIcon.show();
21388                 this.on('resize', this.alignErrorIcon, this);
21389                 break;
21390             default:
21391                 var t = Roo.getDom(this.msgTarget);
21392                 t.innerHTML = msg;
21393                 t.style.display = this.msgDisplay;
21394                 break;
21395         }
21396         this.fireEvent('invalid', this, msg);
21397     },
21398
21399     // private
21400     alignErrorIcon : function(){
21401         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21402     },
21403
21404     /**
21405      * Clear any invalid styles/messages for this field
21406      */
21407     clearInvalid : function(){
21408         if(!this.rendered || this.preventMark){ // not rendered
21409             return;
21410         }
21411         this.el.removeClass(this.invalidClass);
21412         switch(this.msgTarget){
21413             case 'qtip':
21414                 this.el.dom.qtip = '';
21415                 break;
21416             case 'title':
21417                 this.el.dom.title = '';
21418                 break;
21419             case 'under':
21420                 if(this.errorEl){
21421                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21422                 }
21423                 break;
21424             case 'side':
21425                 if(this.errorIcon){
21426                     this.errorIcon.dom.qtip = '';
21427                     this.errorIcon.hide();
21428                     this.un('resize', this.alignErrorIcon, this);
21429                 }
21430                 break;
21431             default:
21432                 var t = Roo.getDom(this.msgTarget);
21433                 t.innerHTML = '';
21434                 t.style.display = 'none';
21435                 break;
21436         }
21437         this.fireEvent('valid', this);
21438     },
21439
21440     /**
21441      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21442      * @return {Mixed} value The field value
21443      */
21444     getRawValue : function(){
21445         var v = this.el.getValue();
21446         if(v === this.emptyText){
21447             v = '';
21448         }
21449         return v;
21450     },
21451
21452     /**
21453      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21454      * @return {Mixed} value The field value
21455      */
21456     getValue : function(){
21457         var v = this.el.getValue();
21458         if(v === this.emptyText || v === undefined){
21459             v = '';
21460         }
21461         return v;
21462     },
21463
21464     /**
21465      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21466      * @param {Mixed} value The value to set
21467      */
21468     setRawValue : function(v){
21469         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21470     },
21471
21472     /**
21473      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21474      * @param {Mixed} value The value to set
21475      */
21476     setValue : function(v){
21477         this.value = v;
21478         if(this.rendered){
21479             this.el.dom.value = (v === null || v === undefined ? '' : v);
21480              this.validate();
21481         }
21482     },
21483
21484     adjustSize : function(w, h){
21485         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21486         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21487         return s;
21488     },
21489
21490     adjustWidth : function(tag, w){
21491         tag = tag.toLowerCase();
21492         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21493             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21494                 if(tag == 'input'){
21495                     return w + 2;
21496                 }
21497                 if(tag = 'textarea'){
21498                     return w-2;
21499                 }
21500             }else if(Roo.isOpera){
21501                 if(tag == 'input'){
21502                     return w + 2;
21503                 }
21504                 if(tag = 'textarea'){
21505                     return w-2;
21506                 }
21507             }
21508         }
21509         return w;
21510     }
21511 });
21512
21513
21514 // anything other than normal should be considered experimental
21515 Roo.form.Field.msgFx = {
21516     normal : {
21517         show: function(msgEl, f){
21518             msgEl.setDisplayed('block');
21519         },
21520
21521         hide : function(msgEl, f){
21522             msgEl.setDisplayed(false).update('');
21523         }
21524     },
21525
21526     slide : {
21527         show: function(msgEl, f){
21528             msgEl.slideIn('t', {stopFx:true});
21529         },
21530
21531         hide : function(msgEl, f){
21532             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21533         }
21534     },
21535
21536     slideRight : {
21537         show: function(msgEl, f){
21538             msgEl.fixDisplay();
21539             msgEl.alignTo(f.el, 'tl-tr');
21540             msgEl.slideIn('l', {stopFx:true});
21541         },
21542
21543         hide : function(msgEl, f){
21544             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21545         }
21546     }
21547 };/*
21548  * Based on:
21549  * Ext JS Library 1.1.1
21550  * Copyright(c) 2006-2007, Ext JS, LLC.
21551  *
21552  * Originally Released Under LGPL - original licence link has changed is not relivant.
21553  *
21554  * Fork - LGPL
21555  * <script type="text/javascript">
21556  */
21557  
21558
21559 /**
21560  * @class Roo.form.TextField
21561  * @extends Roo.form.Field
21562  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21563  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21564  * @constructor
21565  * Creates a new TextField
21566  * @param {Object} config Configuration options
21567  */
21568 Roo.form.TextField = function(config){
21569     Roo.form.TextField.superclass.constructor.call(this, config);
21570     this.addEvents({
21571         /**
21572          * @event autosize
21573          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21574          * according to the default logic, but this event provides a hook for the developer to apply additional
21575          * logic at runtime to resize the field if needed.
21576              * @param {Roo.form.Field} this This text field
21577              * @param {Number} width The new field width
21578              */
21579         autosize : true
21580     });
21581 };
21582
21583 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21584     /**
21585      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21586      */
21587     grow : false,
21588     /**
21589      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21590      */
21591     growMin : 30,
21592     /**
21593      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21594      */
21595     growMax : 800,
21596     /**
21597      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21598      */
21599     vtype : null,
21600     /**
21601      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21602      */
21603     maskRe : null,
21604     /**
21605      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21606      */
21607     disableKeyFilter : false,
21608     /**
21609      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21610      */
21611     allowBlank : true,
21612     /**
21613      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21614      */
21615     minLength : 0,
21616     /**
21617      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21618      */
21619     maxLength : Number.MAX_VALUE,
21620     /**
21621      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21622      */
21623     minLengthText : "The minimum length for this field is {0}",
21624     /**
21625      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21626      */
21627     maxLengthText : "The maximum length for this field is {0}",
21628     /**
21629      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21630      */
21631     selectOnFocus : false,
21632     /**
21633      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21634      */
21635     blankText : "This field is required",
21636     /**
21637      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21638      * If available, this function will be called only after the basic validators all return true, and will be passed the
21639      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21640      */
21641     validator : null,
21642     /**
21643      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21644      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21645      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21646      */
21647     regex : null,
21648     /**
21649      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21650      */
21651     regexText : "",
21652     /**
21653      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
21654      */
21655     emptyText : null,
21656     /**
21657      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
21658      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
21659      */
21660     emptyClass : 'x-form-empty-field',
21661
21662     // private
21663     initEvents : function(){
21664         Roo.form.TextField.superclass.initEvents.call(this);
21665         if(this.validationEvent == 'keyup'){
21666             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21667             this.el.on('keyup', this.filterValidation, this);
21668         }
21669         else if(this.validationEvent !== false){
21670             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21671         }
21672         if(this.selectOnFocus || this.emptyText){
21673             this.on("focus", this.preFocus, this);
21674             if(this.emptyText){
21675                 this.on('blur', this.postBlur, this);
21676                 this.applyEmptyText();
21677             }
21678         }
21679         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21680             this.el.on("keypress", this.filterKeys, this);
21681         }
21682         if(this.grow){
21683             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21684             this.el.on("click", this.autoSize,  this);
21685         }
21686     },
21687
21688     processValue : function(value){
21689         if(this.stripCharsRe){
21690             var newValue = value.replace(this.stripCharsRe, '');
21691             if(newValue !== value){
21692                 this.setRawValue(newValue);
21693                 return newValue;
21694             }
21695         }
21696         return value;
21697     },
21698
21699     filterValidation : function(e){
21700         if(!e.isNavKeyPress()){
21701             this.validationTask.delay(this.validationDelay);
21702         }
21703     },
21704
21705     // private
21706     onKeyUp : function(e){
21707         if(!e.isNavKeyPress()){
21708             this.autoSize();
21709         }
21710     },
21711
21712     /**
21713      * Resets the current field value to the originally-loaded value and clears any validation messages.
21714      * Also adds emptyText and emptyClass if the original value was blank.
21715      */
21716     reset : function(){
21717         Roo.form.TextField.superclass.reset.call(this);
21718         this.applyEmptyText();
21719     },
21720
21721     applyEmptyText : function(){
21722         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
21723             this.setRawValue(this.emptyText);
21724             this.el.addClass(this.emptyClass);
21725         }
21726     },
21727
21728     // private
21729     preFocus : function(){
21730         if(this.emptyText){
21731             if(this.el.dom.value == this.emptyText){
21732                 this.setRawValue('');
21733             }
21734             this.el.removeClass(this.emptyClass);
21735         }
21736         if(this.selectOnFocus){
21737             this.el.dom.select();
21738         }
21739     },
21740
21741     // private
21742     postBlur : function(){
21743         this.applyEmptyText();
21744     },
21745
21746     // private
21747     filterKeys : function(e){
21748         var k = e.getKey();
21749         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21750             return;
21751         }
21752         var c = e.getCharCode(), cc = String.fromCharCode(c);
21753         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21754             return;
21755         }
21756         if(!this.maskRe.test(cc)){
21757             e.stopEvent();
21758         }
21759     },
21760
21761     setValue : function(v){
21762         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
21763             this.el.removeClass(this.emptyClass);
21764         }
21765         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21766         this.applyEmptyText();
21767         this.autoSize();
21768     },
21769
21770     /**
21771      * Validates a value according to the field's validation rules and marks the field as invalid
21772      * if the validation fails
21773      * @param {Mixed} value The value to validate
21774      * @return {Boolean} True if the value is valid, else false
21775      */
21776     validateValue : function(value){
21777         if(value.length < 1 || value === this.emptyText){ // if it's blank
21778              if(this.allowBlank){
21779                 this.clearInvalid();
21780                 return true;
21781              }else{
21782                 this.markInvalid(this.blankText);
21783                 return false;
21784              }
21785         }
21786         if(value.length < this.minLength){
21787             this.markInvalid(String.format(this.minLengthText, this.minLength));
21788             return false;
21789         }
21790         if(value.length > this.maxLength){
21791             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21792             return false;
21793         }
21794         if(this.vtype){
21795             var vt = Roo.form.VTypes;
21796             if(!vt[this.vtype](value, this)){
21797                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21798                 return false;
21799             }
21800         }
21801         if(typeof this.validator == "function"){
21802             var msg = this.validator(value);
21803             if(msg !== true){
21804                 this.markInvalid(msg);
21805                 return false;
21806             }
21807         }
21808         if(this.regex && !this.regex.test(value)){
21809             this.markInvalid(this.regexText);
21810             return false;
21811         }
21812         return true;
21813     },
21814
21815     /**
21816      * Selects text in this field
21817      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21818      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21819      */
21820     selectText : function(start, end){
21821         var v = this.getRawValue();
21822         if(v.length > 0){
21823             start = start === undefined ? 0 : start;
21824             end = end === undefined ? v.length : end;
21825             var d = this.el.dom;
21826             if(d.setSelectionRange){
21827                 d.setSelectionRange(start, end);
21828             }else if(d.createTextRange){
21829                 var range = d.createTextRange();
21830                 range.moveStart("character", start);
21831                 range.moveEnd("character", v.length-end);
21832                 range.select();
21833             }
21834         }
21835     },
21836
21837     /**
21838      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21839      * This only takes effect if grow = true, and fires the autosize event.
21840      */
21841     autoSize : function(){
21842         if(!this.grow || !this.rendered){
21843             return;
21844         }
21845         if(!this.metrics){
21846             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21847         }
21848         var el = this.el;
21849         var v = el.dom.value;
21850         var d = document.createElement('div');
21851         d.appendChild(document.createTextNode(v));
21852         v = d.innerHTML;
21853         d = null;
21854         v += "&#160;";
21855         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21856         this.el.setWidth(w);
21857         this.fireEvent("autosize", this, w);
21858     }
21859 });/*
21860  * Based on:
21861  * Ext JS Library 1.1.1
21862  * Copyright(c) 2006-2007, Ext JS, LLC.
21863  *
21864  * Originally Released Under LGPL - original licence link has changed is not relivant.
21865  *
21866  * Fork - LGPL
21867  * <script type="text/javascript">
21868  */
21869  
21870 /**
21871  * @class Roo.form.Hidden
21872  * @extends Roo.form.TextField
21873  * Simple Hidden element used on forms 
21874  * 
21875  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21876  * 
21877  * @constructor
21878  * Creates a new Hidden form element.
21879  * @param {Object} config Configuration options
21880  */
21881
21882
21883
21884 // easy hidden field...
21885 Roo.form.Hidden = function(config){
21886     Roo.form.Hidden.superclass.constructor.call(this, config);
21887 };
21888   
21889 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21890     fieldLabel:      '',
21891     inputType:      'hidden',
21892     width:          50,
21893     allowBlank:     true,
21894     labelSeparator: '',
21895     hidden:         true,
21896     itemCls :       'x-form-item-display-none'
21897
21898
21899 });
21900
21901
21902 /*
21903  * Based on:
21904  * Ext JS Library 1.1.1
21905  * Copyright(c) 2006-2007, Ext JS, LLC.
21906  *
21907  * Originally Released Under LGPL - original licence link has changed is not relivant.
21908  *
21909  * Fork - LGPL
21910  * <script type="text/javascript">
21911  */
21912  
21913 /**
21914  * @class Roo.form.TriggerField
21915  * @extends Roo.form.TextField
21916  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
21917  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
21918  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
21919  * for which you can provide a custom implementation.  For example:
21920  * <pre><code>
21921 var trigger = new Roo.form.TriggerField();
21922 trigger.onTriggerClick = myTriggerFn;
21923 trigger.applyTo('my-field');
21924 </code></pre>
21925  *
21926  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
21927  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
21928  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
21929  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
21930  * @constructor
21931  * Create a new TriggerField.
21932  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
21933  * to the base TextField)
21934  */
21935 Roo.form.TriggerField = function(config){
21936     this.mimicing = false;
21937     Roo.form.TriggerField.superclass.constructor.call(this, config);
21938 };
21939
21940 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
21941     /**
21942      * @cfg {String} triggerClass A CSS class to apply to the trigger
21943      */
21944     /**
21945      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21946      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
21947      */
21948     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
21949     /**
21950      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
21951      */
21952     hideTrigger:false,
21953
21954     /** @cfg {Boolean} grow @hide */
21955     /** @cfg {Number} growMin @hide */
21956     /** @cfg {Number} growMax @hide */
21957
21958     /**
21959      * @hide 
21960      * @method
21961      */
21962     autoSize: Roo.emptyFn,
21963     // private
21964     monitorTab : true,
21965     // private
21966     deferHeight : true,
21967
21968     
21969     actionMode : 'wrap',
21970     // private
21971     onResize : function(w, h){
21972         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
21973         if(typeof w == 'number'){
21974             var x = w - this.trigger.getWidth();
21975             this.el.setWidth(this.adjustWidth('input', x));
21976             this.trigger.setStyle('left', x+'px');
21977         }
21978     },
21979
21980     // private
21981     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21982
21983     // private
21984     getResizeEl : function(){
21985         return this.wrap;
21986     },
21987
21988     // private
21989     getPositionEl : function(){
21990         return this.wrap;
21991     },
21992
21993     // private
21994     alignErrorIcon : function(){
21995         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
21996     },
21997
21998     // private
21999     onRender : function(ct, position){
22000         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
22001         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
22002         this.trigger = this.wrap.createChild(this.triggerConfig ||
22003                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
22004         if(this.hideTrigger){
22005             this.trigger.setDisplayed(false);
22006         }
22007         this.initTrigger();
22008         if(!this.width){
22009             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
22010         }
22011     },
22012
22013     // private
22014     initTrigger : function(){
22015         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
22016         this.trigger.addClassOnOver('x-form-trigger-over');
22017         this.trigger.addClassOnClick('x-form-trigger-click');
22018     },
22019
22020     // private
22021     onDestroy : function(){
22022         if(this.trigger){
22023             this.trigger.removeAllListeners();
22024             this.trigger.remove();
22025         }
22026         if(this.wrap){
22027             this.wrap.remove();
22028         }
22029         Roo.form.TriggerField.superclass.onDestroy.call(this);
22030     },
22031
22032     // private
22033     onFocus : function(){
22034         Roo.form.TriggerField.superclass.onFocus.call(this);
22035         if(!this.mimicing){
22036             this.wrap.addClass('x-trigger-wrap-focus');
22037             this.mimicing = true;
22038             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
22039             if(this.monitorTab){
22040                 this.el.on("keydown", this.checkTab, this);
22041             }
22042         }
22043     },
22044
22045     // private
22046     checkTab : function(e){
22047         if(e.getKey() == e.TAB){
22048             this.triggerBlur();
22049         }
22050     },
22051
22052     // private
22053     onBlur : function(){
22054         // do nothing
22055     },
22056
22057     // private
22058     mimicBlur : function(e, t){
22059         if(!this.wrap.contains(t) && this.validateBlur()){
22060             this.triggerBlur();
22061         }
22062     },
22063
22064     // private
22065     triggerBlur : function(){
22066         this.mimicing = false;
22067         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
22068         if(this.monitorTab){
22069             this.el.un("keydown", this.checkTab, this);
22070         }
22071         this.wrap.removeClass('x-trigger-wrap-focus');
22072         Roo.form.TriggerField.superclass.onBlur.call(this);
22073     },
22074
22075     // private
22076     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
22077     validateBlur : function(e, t){
22078         return true;
22079     },
22080
22081     // private
22082     onDisable : function(){
22083         Roo.form.TriggerField.superclass.onDisable.call(this);
22084         if(this.wrap){
22085             this.wrap.addClass('x-item-disabled');
22086         }
22087     },
22088
22089     // private
22090     onEnable : function(){
22091         Roo.form.TriggerField.superclass.onEnable.call(this);
22092         if(this.wrap){
22093             this.wrap.removeClass('x-item-disabled');
22094         }
22095     },
22096
22097     // private
22098     onShow : function(){
22099         var ae = this.getActionEl();
22100         
22101         if(ae){
22102             ae.dom.style.display = '';
22103             ae.dom.style.visibility = 'visible';
22104         }
22105     },
22106
22107     // private
22108     
22109     onHide : function(){
22110         var ae = this.getActionEl();
22111         ae.dom.style.display = 'none';
22112     },
22113
22114     /**
22115      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
22116      * by an implementing function.
22117      * @method
22118      * @param {EventObject} e
22119      */
22120     onTriggerClick : Roo.emptyFn
22121 });
22122
22123 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
22124 // to be extended by an implementing class.  For an example of implementing this class, see the custom
22125 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
22126 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
22127     initComponent : function(){
22128         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
22129
22130         this.triggerConfig = {
22131             tag:'span', cls:'x-form-twin-triggers', cn:[
22132             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
22133             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
22134         ]};
22135     },
22136
22137     getTrigger : function(index){
22138         return this.triggers[index];
22139     },
22140
22141     initTrigger : function(){
22142         var ts = this.trigger.select('.x-form-trigger', true);
22143         this.wrap.setStyle('overflow', 'hidden');
22144         var triggerField = this;
22145         ts.each(function(t, all, index){
22146             t.hide = function(){
22147                 var w = triggerField.wrap.getWidth();
22148                 this.dom.style.display = 'none';
22149                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22150             };
22151             t.show = function(){
22152                 var w = triggerField.wrap.getWidth();
22153                 this.dom.style.display = '';
22154                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22155             };
22156             var triggerIndex = 'Trigger'+(index+1);
22157
22158             if(this['hide'+triggerIndex]){
22159                 t.dom.style.display = 'none';
22160             }
22161             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
22162             t.addClassOnOver('x-form-trigger-over');
22163             t.addClassOnClick('x-form-trigger-click');
22164         }, this);
22165         this.triggers = ts.elements;
22166     },
22167
22168     onTrigger1Click : Roo.emptyFn,
22169     onTrigger2Click : Roo.emptyFn
22170 });/*
22171  * Based on:
22172  * Ext JS Library 1.1.1
22173  * Copyright(c) 2006-2007, Ext JS, LLC.
22174  *
22175  * Originally Released Under LGPL - original licence link has changed is not relivant.
22176  *
22177  * Fork - LGPL
22178  * <script type="text/javascript">
22179  */
22180  
22181 /**
22182  * @class Roo.form.TextArea
22183  * @extends Roo.form.TextField
22184  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
22185  * support for auto-sizing.
22186  * @constructor
22187  * Creates a new TextArea
22188  * @param {Object} config Configuration options
22189  */
22190 Roo.form.TextArea = function(config){
22191     Roo.form.TextArea.superclass.constructor.call(this, config);
22192     // these are provided exchanges for backwards compat
22193     // minHeight/maxHeight were replaced by growMin/growMax to be
22194     // compatible with TextField growing config values
22195     if(this.minHeight !== undefined){
22196         this.growMin = this.minHeight;
22197     }
22198     if(this.maxHeight !== undefined){
22199         this.growMax = this.maxHeight;
22200     }
22201 };
22202
22203 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
22204     /**
22205      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
22206      */
22207     growMin : 60,
22208     /**
22209      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
22210      */
22211     growMax: 1000,
22212     /**
22213      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
22214      * in the field (equivalent to setting overflow: hidden, defaults to false)
22215      */
22216     preventScrollbars: false,
22217     /**
22218      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22219      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
22220      */
22221
22222     // private
22223     onRender : function(ct, position){
22224         if(!this.el){
22225             this.defaultAutoCreate = {
22226                 tag: "textarea",
22227                 style:"width:300px;height:60px;",
22228                 autocomplete: "off"
22229             };
22230         }
22231         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
22232         if(this.grow){
22233             this.textSizeEl = Roo.DomHelper.append(document.body, {
22234                 tag: "pre", cls: "x-form-grow-sizer"
22235             });
22236             if(this.preventScrollbars){
22237                 this.el.setStyle("overflow", "hidden");
22238             }
22239             this.el.setHeight(this.growMin);
22240         }
22241     },
22242
22243     onDestroy : function(){
22244         if(this.textSizeEl){
22245             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
22246         }
22247         Roo.form.TextArea.superclass.onDestroy.call(this);
22248     },
22249
22250     // private
22251     onKeyUp : function(e){
22252         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
22253             this.autoSize();
22254         }
22255     },
22256
22257     /**
22258      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
22259      * This only takes effect if grow = true, and fires the autosize event if the height changes.
22260      */
22261     autoSize : function(){
22262         if(!this.grow || !this.textSizeEl){
22263             return;
22264         }
22265         var el = this.el;
22266         var v = el.dom.value;
22267         var ts = this.textSizeEl;
22268
22269         ts.innerHTML = '';
22270         ts.appendChild(document.createTextNode(v));
22271         v = ts.innerHTML;
22272
22273         Roo.fly(ts).setWidth(this.el.getWidth());
22274         if(v.length < 1){
22275             v = "&#160;&#160;";
22276         }else{
22277             if(Roo.isIE){
22278                 v = v.replace(/\n/g, '<p>&#160;</p>');
22279             }
22280             v += "&#160;\n&#160;";
22281         }
22282         ts.innerHTML = v;
22283         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
22284         if(h != this.lastHeight){
22285             this.lastHeight = h;
22286             this.el.setHeight(h);
22287             this.fireEvent("autosize", this, h);
22288         }
22289     }
22290 });/*
22291  * Based on:
22292  * Ext JS Library 1.1.1
22293  * Copyright(c) 2006-2007, Ext JS, LLC.
22294  *
22295  * Originally Released Under LGPL - original licence link has changed is not relivant.
22296  *
22297  * Fork - LGPL
22298  * <script type="text/javascript">
22299  */
22300  
22301
22302 /**
22303  * @class Roo.form.NumberField
22304  * @extends Roo.form.TextField
22305  * Numeric text field that provides automatic keystroke filtering and numeric validation.
22306  * @constructor
22307  * Creates a new NumberField
22308  * @param {Object} config Configuration options
22309  */
22310 Roo.form.NumberField = function(config){
22311     Roo.form.NumberField.superclass.constructor.call(this, config);
22312 };
22313
22314 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
22315     /**
22316      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22317      */
22318     fieldClass: "x-form-field x-form-num-field",
22319     /**
22320      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22321      */
22322     allowDecimals : true,
22323     /**
22324      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22325      */
22326     decimalSeparator : ".",
22327     /**
22328      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22329      */
22330     decimalPrecision : 2,
22331     /**
22332      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22333      */
22334     allowNegative : true,
22335     /**
22336      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22337      */
22338     minValue : Number.NEGATIVE_INFINITY,
22339     /**
22340      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22341      */
22342     maxValue : Number.MAX_VALUE,
22343     /**
22344      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22345      */
22346     minText : "The minimum value for this field is {0}",
22347     /**
22348      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22349      */
22350     maxText : "The maximum value for this field is {0}",
22351     /**
22352      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22353      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22354      */
22355     nanText : "{0} is not a valid number",
22356
22357     // private
22358     initEvents : function(){
22359         Roo.form.NumberField.superclass.initEvents.call(this);
22360         var allowed = "0123456789";
22361         if(this.allowDecimals){
22362             allowed += this.decimalSeparator;
22363         }
22364         if(this.allowNegative){
22365             allowed += "-";
22366         }
22367         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22368         var keyPress = function(e){
22369             var k = e.getKey();
22370             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22371                 return;
22372             }
22373             var c = e.getCharCode();
22374             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22375                 e.stopEvent();
22376             }
22377         };
22378         this.el.on("keypress", keyPress, this);
22379     },
22380
22381     // private
22382     validateValue : function(value){
22383         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22384             return false;
22385         }
22386         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22387              return true;
22388         }
22389         var num = this.parseValue(value);
22390         if(isNaN(num)){
22391             this.markInvalid(String.format(this.nanText, value));
22392             return false;
22393         }
22394         if(num < this.minValue){
22395             this.markInvalid(String.format(this.minText, this.minValue));
22396             return false;
22397         }
22398         if(num > this.maxValue){
22399             this.markInvalid(String.format(this.maxText, this.maxValue));
22400             return false;
22401         }
22402         return true;
22403     },
22404
22405     getValue : function(){
22406         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22407     },
22408
22409     // private
22410     parseValue : function(value){
22411         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22412         return isNaN(value) ? '' : value;
22413     },
22414
22415     // private
22416     fixPrecision : function(value){
22417         var nan = isNaN(value);
22418         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22419             return nan ? '' : value;
22420         }
22421         return parseFloat(value).toFixed(this.decimalPrecision);
22422     },
22423
22424     setValue : function(v){
22425         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22426     },
22427
22428     // private
22429     decimalPrecisionFcn : function(v){
22430         return Math.floor(v);
22431     },
22432
22433     beforeBlur : function(){
22434         var v = this.parseValue(this.getRawValue());
22435         if(v){
22436             this.setValue(this.fixPrecision(v));
22437         }
22438     }
22439 });/*
22440  * Based on:
22441  * Ext JS Library 1.1.1
22442  * Copyright(c) 2006-2007, Ext JS, LLC.
22443  *
22444  * Originally Released Under LGPL - original licence link has changed is not relivant.
22445  *
22446  * Fork - LGPL
22447  * <script type="text/javascript">
22448  */
22449  
22450 /**
22451  * @class Roo.form.DateField
22452  * @extends Roo.form.TriggerField
22453  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22454 * @constructor
22455 * Create a new DateField
22456 * @param {Object} config
22457  */
22458 Roo.form.DateField = function(config){
22459     Roo.form.DateField.superclass.constructor.call(this, config);
22460     
22461       this.addEvents({
22462          
22463         /**
22464          * @event select
22465          * Fires when a date is selected
22466              * @param {Roo.form.DateField} combo This combo box
22467              * @param {Date} date The date selected
22468              */
22469         'select' : true
22470          
22471     });
22472     
22473     
22474     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22475     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22476     this.ddMatch = null;
22477     if(this.disabledDates){
22478         var dd = this.disabledDates;
22479         var re = "(?:";
22480         for(var i = 0; i < dd.length; i++){
22481             re += dd[i];
22482             if(i != dd.length-1) re += "|";
22483         }
22484         this.ddMatch = new RegExp(re + ")");
22485     }
22486 };
22487
22488 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22489     /**
22490      * @cfg {String} format
22491      * The default date format string which can be overriden for localization support.  The format must be
22492      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22493      */
22494     format : "m/d/y",
22495     /**
22496      * @cfg {String} altFormats
22497      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22498      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22499      */
22500     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22501     /**
22502      * @cfg {Array} disabledDays
22503      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22504      */
22505     disabledDays : null,
22506     /**
22507      * @cfg {String} disabledDaysText
22508      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22509      */
22510     disabledDaysText : "Disabled",
22511     /**
22512      * @cfg {Array} disabledDates
22513      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22514      * expression so they are very powerful. Some examples:
22515      * <ul>
22516      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22517      * <li>["03/08", "09/16"] would disable those days for every year</li>
22518      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22519      * <li>["03/../2006"] would disable every day in March 2006</li>
22520      * <li>["^03"] would disable every day in every March</li>
22521      * </ul>
22522      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22523      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22524      */
22525     disabledDates : null,
22526     /**
22527      * @cfg {String} disabledDatesText
22528      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22529      */
22530     disabledDatesText : "Disabled",
22531     /**
22532      * @cfg {Date/String} minValue
22533      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22534      * valid format (defaults to null).
22535      */
22536     minValue : null,
22537     /**
22538      * @cfg {Date/String} maxValue
22539      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22540      * valid format (defaults to null).
22541      */
22542     maxValue : null,
22543     /**
22544      * @cfg {String} minText
22545      * The error text to display when the date in the cell is before minValue (defaults to
22546      * 'The date in this field must be after {minValue}').
22547      */
22548     minText : "The date in this field must be equal to or after {0}",
22549     /**
22550      * @cfg {String} maxText
22551      * The error text to display when the date in the cell is after maxValue (defaults to
22552      * 'The date in this field must be before {maxValue}').
22553      */
22554     maxText : "The date in this field must be equal to or before {0}",
22555     /**
22556      * @cfg {String} invalidText
22557      * The error text to display when the date in the field is invalid (defaults to
22558      * '{value} is not a valid date - it must be in the format {format}').
22559      */
22560     invalidText : "{0} is not a valid date - it must be in the format {1}",
22561     /**
22562      * @cfg {String} triggerClass
22563      * An additional CSS class used to style the trigger button.  The trigger will always get the
22564      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22565      * which displays a calendar icon).
22566      */
22567     triggerClass : 'x-form-date-trigger',
22568     
22569
22570     /**
22571      * @cfg {bool} useIso
22572      * if enabled, then the date field will use a hidden field to store the 
22573      * real value as iso formated date. default (false)
22574      */ 
22575     useIso : false,
22576     /**
22577      * @cfg {String/Object} autoCreate
22578      * A DomHelper element spec, or true for a default element spec (defaults to
22579      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22580      */ 
22581     // private
22582     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22583     
22584     // private
22585     hiddenField: false,
22586     
22587     onRender : function(ct, position)
22588     {
22589         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22590         if (this.useIso) {
22591             this.el.dom.removeAttribute('name'); 
22592             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22593                     'before', true);
22594             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
22595             // prevent input submission
22596             this.hiddenName = this.name;
22597         }
22598             
22599             
22600     },
22601     
22602     // private
22603     validateValue : function(value)
22604     {
22605         value = this.formatDate(value);
22606         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22607             return false;
22608         }
22609         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22610              return true;
22611         }
22612         var svalue = value;
22613         value = this.parseDate(value);
22614         if(!value){
22615             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22616             return false;
22617         }
22618         var time = value.getTime();
22619         if(this.minValue && time < this.minValue.getTime()){
22620             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22621             return false;
22622         }
22623         if(this.maxValue && time > this.maxValue.getTime()){
22624             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22625             return false;
22626         }
22627         if(this.disabledDays){
22628             var day = value.getDay();
22629             for(var i = 0; i < this.disabledDays.length; i++) {
22630                 if(day === this.disabledDays[i]){
22631                     this.markInvalid(this.disabledDaysText);
22632                     return false;
22633                 }
22634             }
22635         }
22636         var fvalue = this.formatDate(value);
22637         if(this.ddMatch && this.ddMatch.test(fvalue)){
22638             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22639             return false;
22640         }
22641         return true;
22642     },
22643
22644     // private
22645     // Provides logic to override the default TriggerField.validateBlur which just returns true
22646     validateBlur : function(){
22647         return !this.menu || !this.menu.isVisible();
22648     },
22649
22650     /**
22651      * Returns the current date value of the date field.
22652      * @return {Date} The date value
22653      */
22654     getValue : function(){
22655         
22656         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22657     },
22658
22659     /**
22660      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22661      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22662      * (the default format used is "m/d/y").
22663      * <br />Usage:
22664      * <pre><code>
22665 //All of these calls set the same date value (May 4, 2006)
22666
22667 //Pass a date object:
22668 var dt = new Date('5/4/06');
22669 dateField.setValue(dt);
22670
22671 //Pass a date string (default format):
22672 dateField.setValue('5/4/06');
22673
22674 //Pass a date string (custom format):
22675 dateField.format = 'Y-m-d';
22676 dateField.setValue('2006-5-4');
22677 </code></pre>
22678      * @param {String/Date} date The date or valid date string
22679      */
22680     setValue : function(date){
22681         if (this.hiddenField) {
22682             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22683         }
22684         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22685     },
22686
22687     // private
22688     parseDate : function(value){
22689         if(!value || value instanceof Date){
22690             return value;
22691         }
22692         var v = Date.parseDate(value, this.format);
22693         if(!v && this.altFormats){
22694             if(!this.altFormatsArray){
22695                 this.altFormatsArray = this.altFormats.split("|");
22696             }
22697             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22698                 v = Date.parseDate(value, this.altFormatsArray[i]);
22699             }
22700         }
22701         return v;
22702     },
22703
22704     // private
22705     formatDate : function(date, fmt){
22706         return (!date || !(date instanceof Date)) ?
22707                date : date.dateFormat(fmt || this.format);
22708     },
22709
22710     // private
22711     menuListeners : {
22712         select: function(m, d){
22713             this.setValue(d);
22714             this.fireEvent('select', this, d);
22715         },
22716         show : function(){ // retain focus styling
22717             this.onFocus();
22718         },
22719         hide : function(){
22720             this.focus.defer(10, this);
22721             var ml = this.menuListeners;
22722             this.menu.un("select", ml.select,  this);
22723             this.menu.un("show", ml.show,  this);
22724             this.menu.un("hide", ml.hide,  this);
22725         }
22726     },
22727
22728     // private
22729     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22730     onTriggerClick : function(){
22731         if(this.disabled){
22732             return;
22733         }
22734         if(this.menu == null){
22735             this.menu = new Roo.menu.DateMenu();
22736         }
22737         Roo.apply(this.menu.picker,  {
22738             showClear: this.allowBlank,
22739             minDate : this.minValue,
22740             maxDate : this.maxValue,
22741             disabledDatesRE : this.ddMatch,
22742             disabledDatesText : this.disabledDatesText,
22743             disabledDays : this.disabledDays,
22744             disabledDaysText : this.disabledDaysText,
22745             format : this.format,
22746             minText : String.format(this.minText, this.formatDate(this.minValue)),
22747             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22748         });
22749         this.menu.on(Roo.apply({}, this.menuListeners, {
22750             scope:this
22751         }));
22752         this.menu.picker.setValue(this.getValue() || new Date());
22753         this.menu.show(this.el, "tl-bl?");
22754     },
22755
22756     beforeBlur : function(){
22757         var v = this.parseDate(this.getRawValue());
22758         if(v){
22759             this.setValue(v);
22760         }
22761     }
22762
22763     /** @cfg {Boolean} grow @hide */
22764     /** @cfg {Number} growMin @hide */
22765     /** @cfg {Number} growMax @hide */
22766     /**
22767      * @hide
22768      * @method autoSize
22769      */
22770 });/*
22771  * Based on:
22772  * Ext JS Library 1.1.1
22773  * Copyright(c) 2006-2007, Ext JS, LLC.
22774  *
22775  * Originally Released Under LGPL - original licence link has changed is not relivant.
22776  *
22777  * Fork - LGPL
22778  * <script type="text/javascript">
22779  */
22780  
22781
22782 /**
22783  * @class Roo.form.ComboBox
22784  * @extends Roo.form.TriggerField
22785  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22786  * @constructor
22787  * Create a new ComboBox.
22788  * @param {Object} config Configuration options
22789  */
22790 Roo.form.ComboBox = function(config){
22791     Roo.form.ComboBox.superclass.constructor.call(this, config);
22792     this.addEvents({
22793         /**
22794          * @event expand
22795          * Fires when the dropdown list is expanded
22796              * @param {Roo.form.ComboBox} combo This combo box
22797              */
22798         'expand' : true,
22799         /**
22800          * @event collapse
22801          * Fires when the dropdown list is collapsed
22802              * @param {Roo.form.ComboBox} combo This combo box
22803              */
22804         'collapse' : true,
22805         /**
22806          * @event beforeselect
22807          * Fires before a list item is selected. Return false to cancel the selection.
22808              * @param {Roo.form.ComboBox} combo This combo box
22809              * @param {Roo.data.Record} record The data record returned from the underlying store
22810              * @param {Number} index The index of the selected item in the dropdown list
22811              */
22812         'beforeselect' : true,
22813         /**
22814          * @event select
22815          * Fires when a list item is selected
22816              * @param {Roo.form.ComboBox} combo This combo box
22817              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22818              * @param {Number} index The index of the selected item in the dropdown list
22819              */
22820         'select' : true,
22821         /**
22822          * @event beforequery
22823          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22824          * The event object passed has these properties:
22825              * @param {Roo.form.ComboBox} combo This combo box
22826              * @param {String} query The query
22827              * @param {Boolean} forceAll true to force "all" query
22828              * @param {Boolean} cancel true to cancel the query
22829              * @param {Object} e The query event object
22830              */
22831         'beforequery': true,
22832          /**
22833          * @event add
22834          * Fires when the 'add' icon is pressed (add a listener to enable add button)
22835              * @param {Roo.form.ComboBox} combo This combo box
22836              */
22837         'add' : true,
22838         /**
22839          * @event edit
22840          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
22841              * @param {Roo.form.ComboBox} combo This combo box
22842              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
22843              */
22844         'edit' : true
22845         
22846         
22847     });
22848     if(this.transform){
22849         this.allowDomMove = false;
22850         var s = Roo.getDom(this.transform);
22851         if(!this.hiddenName){
22852             this.hiddenName = s.name;
22853         }
22854         if(!this.store){
22855             this.mode = 'local';
22856             var d = [], opts = s.options;
22857             for(var i = 0, len = opts.length;i < len; i++){
22858                 var o = opts[i];
22859                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22860                 if(o.selected) {
22861                     this.value = value;
22862                 }
22863                 d.push([value, o.text]);
22864             }
22865             this.store = new Roo.data.SimpleStore({
22866                 'id': 0,
22867                 fields: ['value', 'text'],
22868                 data : d
22869             });
22870             this.valueField = 'value';
22871             this.displayField = 'text';
22872         }
22873         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22874         if(!this.lazyRender){
22875             this.target = true;
22876             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22877             s.parentNode.removeChild(s); // remove it
22878             this.render(this.el.parentNode);
22879         }else{
22880             s.parentNode.removeChild(s); // remove it
22881         }
22882
22883     }
22884     if (this.store) {
22885         this.store = Roo.factory(this.store, Roo.data);
22886     }
22887     
22888     this.selectedIndex = -1;
22889     if(this.mode == 'local'){
22890         if(config.queryDelay === undefined){
22891             this.queryDelay = 10;
22892         }
22893         if(config.minChars === undefined){
22894             this.minChars = 0;
22895         }
22896     }
22897 };
22898
22899 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22900     /**
22901      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22902      */
22903     /**
22904      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
22905      * rendering into an Roo.Editor, defaults to false)
22906      */
22907     /**
22908      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
22909      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
22910      */
22911     /**
22912      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
22913      */
22914     /**
22915      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
22916      * the dropdown list (defaults to undefined, with no header element)
22917      */
22918
22919      /**
22920      * @cfg {String/Roo.Template} tpl The template to use to render the output
22921      */
22922      
22923     // private
22924     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
22925     /**
22926      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
22927      */
22928     listWidth: undefined,
22929     /**
22930      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
22931      * mode = 'remote' or 'text' if mode = 'local')
22932      */
22933     displayField: undefined,
22934     /**
22935      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
22936      * mode = 'remote' or 'value' if mode = 'local'). 
22937      * Note: use of a valueField requires the user make a selection
22938      * in order for a value to be mapped.
22939      */
22940     valueField: undefined,
22941     
22942     
22943     /**
22944      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
22945      * field's data value (defaults to the underlying DOM element's name)
22946      */
22947     hiddenName: undefined,
22948     /**
22949      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
22950      */
22951     listClass: '',
22952     /**
22953      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
22954      */
22955     selectedClass: 'x-combo-selected',
22956     /**
22957      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22958      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
22959      * which displays a downward arrow icon).
22960      */
22961     triggerClass : 'x-form-arrow-trigger',
22962     /**
22963      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
22964      */
22965     shadow:'sides',
22966     /**
22967      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
22968      * anchor positions (defaults to 'tl-bl')
22969      */
22970     listAlign: 'tl-bl?',
22971     /**
22972      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
22973      */
22974     maxHeight: 300,
22975     /**
22976      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
22977      * query specified by the allQuery config option (defaults to 'query')
22978      */
22979     triggerAction: 'query',
22980     /**
22981      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
22982      * (defaults to 4, does not apply if editable = false)
22983      */
22984     minChars : 4,
22985     /**
22986      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
22987      * delay (typeAheadDelay) if it matches a known value (defaults to false)
22988      */
22989     typeAhead: false,
22990     /**
22991      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
22992      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
22993      */
22994     queryDelay: 500,
22995     /**
22996      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
22997      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
22998      */
22999     pageSize: 0,
23000     /**
23001      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
23002      * when editable = true (defaults to false)
23003      */
23004     selectOnFocus:false,
23005     /**
23006      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
23007      */
23008     queryParam: 'query',
23009     /**
23010      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
23011      * when mode = 'remote' (defaults to 'Loading...')
23012      */
23013     loadingText: 'Loading...',
23014     /**
23015      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
23016      */
23017     resizable: false,
23018     /**
23019      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
23020      */
23021     handleHeight : 8,
23022     /**
23023      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
23024      * traditional select (defaults to true)
23025      */
23026     editable: true,
23027     /**
23028      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
23029      */
23030     allQuery: '',
23031     /**
23032      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
23033      */
23034     mode: 'remote',
23035     /**
23036      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
23037      * listWidth has a higher value)
23038      */
23039     minListWidth : 70,
23040     /**
23041      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
23042      * allow the user to set arbitrary text into the field (defaults to false)
23043      */
23044     forceSelection:false,
23045     /**
23046      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
23047      * if typeAhead = true (defaults to 250)
23048      */
23049     typeAheadDelay : 250,
23050     /**
23051      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
23052      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
23053      */
23054     valueNotFoundText : undefined,
23055     /**
23056      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
23057      */
23058     blockFocus : false,
23059     
23060     /**
23061      * @cfg {Boolean} disableClear Disable showing of clear button.
23062      */
23063     disableClear : false,
23064     /**
23065      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
23066      */
23067     alwaysQuery : false,
23068     
23069     //private
23070     addicon : false,
23071     editicon: false,
23072     
23073     // element that contains real text value.. (when hidden is used..)
23074      
23075     // private
23076     onRender : function(ct, position){
23077         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
23078         if(this.hiddenName){
23079             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
23080                     'before', true);
23081             this.hiddenField.value =
23082                 this.hiddenValue !== undefined ? this.hiddenValue :
23083                 this.value !== undefined ? this.value : '';
23084
23085             // prevent input submission
23086             this.el.dom.removeAttribute('name');
23087              
23088              
23089         }
23090         if(Roo.isGecko){
23091             this.el.dom.setAttribute('autocomplete', 'off');
23092         }
23093
23094         var cls = 'x-combo-list';
23095
23096         this.list = new Roo.Layer({
23097             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
23098         });
23099
23100         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
23101         this.list.setWidth(lw);
23102         this.list.swallowEvent('mousewheel');
23103         this.assetHeight = 0;
23104
23105         if(this.title){
23106             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
23107             this.assetHeight += this.header.getHeight();
23108         }
23109
23110         this.innerList = this.list.createChild({cls:cls+'-inner'});
23111         this.innerList.on('mouseover', this.onViewOver, this);
23112         this.innerList.on('mousemove', this.onViewMove, this);
23113         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23114         
23115         if(this.allowBlank && !this.pageSize && !this.disableClear){
23116             this.footer = this.list.createChild({cls:cls+'-ft'});
23117             this.pageTb = new Roo.Toolbar(this.footer);
23118            
23119         }
23120         if(this.pageSize){
23121             this.footer = this.list.createChild({cls:cls+'-ft'});
23122             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
23123                     {pageSize: this.pageSize});
23124             
23125         }
23126         
23127         if (this.pageTb && this.allowBlank && !this.disableClear) {
23128             var _this = this;
23129             this.pageTb.add(new Roo.Toolbar.Fill(), {
23130                 cls: 'x-btn-icon x-btn-clear',
23131                 text: '&#160;',
23132                 handler: function()
23133                 {
23134                     _this.collapse();
23135                     _this.clearValue();
23136                     _this.onSelect(false, -1);
23137                 }
23138             });
23139         }
23140         if (this.footer) {
23141             this.assetHeight += this.footer.getHeight();
23142         }
23143         
23144
23145         if(!this.tpl){
23146             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
23147         }
23148
23149         this.view = new Roo.View(this.innerList, this.tpl, {
23150             singleSelect:true, store: this.store, selectedClass: this.selectedClass
23151         });
23152
23153         this.view.on('click', this.onViewClick, this);
23154
23155         this.store.on('beforeload', this.onBeforeLoad, this);
23156         this.store.on('load', this.onLoad, this);
23157         this.store.on('loadexception', this.onLoadException, this);
23158
23159         if(this.resizable){
23160             this.resizer = new Roo.Resizable(this.list,  {
23161                pinned:true, handles:'se'
23162             });
23163             this.resizer.on('resize', function(r, w, h){
23164                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23165                 this.listWidth = w;
23166                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23167                 this.restrictHeight();
23168             }, this);
23169             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23170         }
23171         if(!this.editable){
23172             this.editable = true;
23173             this.setEditable(false);
23174         }  
23175         
23176         
23177         if (typeof(this.events.add.listeners) != 'undefined') {
23178             
23179             this.addicon = this.wrap.createChild(
23180                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
23181        
23182             this.addicon.on('click', function(e) {
23183                 this.fireEvent('add', this);
23184             }, this);
23185         }
23186         if (typeof(this.events.edit.listeners) != 'undefined') {
23187             
23188             this.editicon = this.wrap.createChild(
23189                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
23190             if (this.addicon) {
23191                 this.editicon.setStyle('margin-left', '40px');
23192             }
23193             this.editicon.on('click', function(e) {
23194                 
23195                 // we fire even  if inothing is selected..
23196                 this.fireEvent('edit', this, this.lastData );
23197                 
23198             }, this);
23199         }
23200         
23201         
23202         
23203     },
23204
23205     // private
23206     initEvents : function(){
23207         Roo.form.ComboBox.superclass.initEvents.call(this);
23208
23209         this.keyNav = new Roo.KeyNav(this.el, {
23210             "up" : function(e){
23211                 this.inKeyMode = true;
23212                 this.selectPrev();
23213             },
23214
23215             "down" : function(e){
23216                 if(!this.isExpanded()){
23217                     this.onTriggerClick();
23218                 }else{
23219                     this.inKeyMode = true;
23220                     this.selectNext();
23221                 }
23222             },
23223
23224             "enter" : function(e){
23225                 this.onViewClick();
23226                 //return true;
23227             },
23228
23229             "esc" : function(e){
23230                 this.collapse();
23231             },
23232
23233             "tab" : function(e){
23234                 this.onViewClick(false);
23235                 this.fireEvent("specialkey", this, e);
23236                 return true;
23237             },
23238
23239             scope : this,
23240
23241             doRelay : function(foo, bar, hname){
23242                 if(hname == 'down' || this.scope.isExpanded()){
23243                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23244                 }
23245                 return true;
23246             },
23247
23248             forceKeyDown: true
23249         });
23250         this.queryDelay = Math.max(this.queryDelay || 10,
23251                 this.mode == 'local' ? 10 : 250);
23252         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23253         if(this.typeAhead){
23254             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23255         }
23256         if(this.editable !== false){
23257             this.el.on("keyup", this.onKeyUp, this);
23258         }
23259         if(this.forceSelection){
23260             this.on('blur', this.doForce, this);
23261         }
23262     },
23263
23264     onDestroy : function(){
23265         if(this.view){
23266             this.view.setStore(null);
23267             this.view.el.removeAllListeners();
23268             this.view.el.remove();
23269             this.view.purgeListeners();
23270         }
23271         if(this.list){
23272             this.list.destroy();
23273         }
23274         if(this.store){
23275             this.store.un('beforeload', this.onBeforeLoad, this);
23276             this.store.un('load', this.onLoad, this);
23277             this.store.un('loadexception', this.onLoadException, this);
23278         }
23279         Roo.form.ComboBox.superclass.onDestroy.call(this);
23280     },
23281
23282     // private
23283     fireKey : function(e){
23284         if(e.isNavKeyPress() && !this.list.isVisible()){
23285             this.fireEvent("specialkey", this, e);
23286         }
23287     },
23288
23289     // private
23290     onResize: function(w, h){
23291         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23292         
23293         if(typeof w != 'number'){
23294             // we do not handle it!?!?
23295             return;
23296         }
23297         var tw = this.trigger.getWidth();
23298         tw += this.addicon ? this.addicon.getWidth() : 0;
23299         tw += this.editicon ? this.editicon.getWidth() : 0;
23300         var x = w - tw;
23301         this.el.setWidth( this.adjustWidth('input', x));
23302             
23303         this.trigger.setStyle('left', x+'px');
23304         
23305         if(this.list && this.listWidth === undefined){
23306             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23307             this.list.setWidth(lw);
23308             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23309         }
23310         
23311     
23312         
23313     },
23314
23315     /**
23316      * Allow or prevent the user from directly editing the field text.  If false is passed,
23317      * the user will only be able to select from the items defined in the dropdown list.  This method
23318      * is the runtime equivalent of setting the 'editable' config option at config time.
23319      * @param {Boolean} value True to allow the user to directly edit the field text
23320      */
23321     setEditable : function(value){
23322         if(value == this.editable){
23323             return;
23324         }
23325         this.editable = value;
23326         if(!value){
23327             this.el.dom.setAttribute('readOnly', true);
23328             this.el.on('mousedown', this.onTriggerClick,  this);
23329             this.el.addClass('x-combo-noedit');
23330         }else{
23331             this.el.dom.setAttribute('readOnly', false);
23332             this.el.un('mousedown', this.onTriggerClick,  this);
23333             this.el.removeClass('x-combo-noedit');
23334         }
23335     },
23336
23337     // private
23338     onBeforeLoad : function(){
23339         if(!this.hasFocus){
23340             return;
23341         }
23342         this.innerList.update(this.loadingText ?
23343                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23344         this.restrictHeight();
23345         this.selectedIndex = -1;
23346     },
23347
23348     // private
23349     onLoad : function(){
23350         if(!this.hasFocus){
23351             return;
23352         }
23353         if(this.store.getCount() > 0){
23354             this.expand();
23355             this.restrictHeight();
23356             if(this.lastQuery == this.allQuery){
23357                 if(this.editable){
23358                     this.el.dom.select();
23359                 }
23360                 if(!this.selectByValue(this.value, true)){
23361                     this.select(0, true);
23362                 }
23363             }else{
23364                 this.selectNext();
23365                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23366                     this.taTask.delay(this.typeAheadDelay);
23367                 }
23368             }
23369         }else{
23370             this.onEmptyResults();
23371         }
23372         //this.el.focus();
23373     },
23374     // private
23375     onLoadException : function()
23376     {
23377         this.collapse();
23378         Roo.log(this.store.reader.jsonData);
23379         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
23380             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
23381         }
23382         
23383         
23384     },
23385     // private
23386     onTypeAhead : function(){
23387         if(this.store.getCount() > 0){
23388             var r = this.store.getAt(0);
23389             var newValue = r.data[this.displayField];
23390             var len = newValue.length;
23391             var selStart = this.getRawValue().length;
23392             if(selStart != len){
23393                 this.setRawValue(newValue);
23394                 this.selectText(selStart, newValue.length);
23395             }
23396         }
23397     },
23398
23399     // private
23400     onSelect : function(record, index){
23401         if(this.fireEvent('beforeselect', this, record, index) !== false){
23402             this.setFromData(index > -1 ? record.data : false);
23403             this.collapse();
23404             this.fireEvent('select', this, record, index);
23405         }
23406     },
23407
23408     /**
23409      * Returns the currently selected field value or empty string if no value is set.
23410      * @return {String} value The selected value
23411      */
23412     getValue : function(){
23413         if(this.valueField){
23414             return typeof this.value != 'undefined' ? this.value : '';
23415         }else{
23416             return Roo.form.ComboBox.superclass.getValue.call(this);
23417         }
23418     },
23419
23420     /**
23421      * Clears any text/value currently set in the field
23422      */
23423     clearValue : function(){
23424         if(this.hiddenField){
23425             this.hiddenField.value = '';
23426         }
23427         this.value = '';
23428         this.setRawValue('');
23429         this.lastSelectionText = '';
23430         this.applyEmptyText();
23431     },
23432
23433     /**
23434      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23435      * will be displayed in the field.  If the value does not match the data value of an existing item,
23436      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23437      * Otherwise the field will be blank (although the value will still be set).
23438      * @param {String} value The value to match
23439      */
23440     setValue : function(v){
23441         var text = v;
23442         if(this.valueField){
23443             var r = this.findRecord(this.valueField, v);
23444             if(r){
23445                 text = r.data[this.displayField];
23446             }else if(this.valueNotFoundText !== undefined){
23447                 text = this.valueNotFoundText;
23448             }
23449         }
23450         this.lastSelectionText = text;
23451         if(this.hiddenField){
23452             this.hiddenField.value = v;
23453         }
23454         Roo.form.ComboBox.superclass.setValue.call(this, text);
23455         this.value = v;
23456     },
23457     /**
23458      * @property {Object} the last set data for the element
23459      */
23460     
23461     lastData : false,
23462     /**
23463      * Sets the value of the field based on a object which is related to the record format for the store.
23464      * @param {Object} value the value to set as. or false on reset?
23465      */
23466     setFromData : function(o){
23467         var dv = ''; // display value
23468         var vv = ''; // value value..
23469         this.lastData = o;
23470         if (this.displayField) {
23471             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23472         } else {
23473             // this is an error condition!!!
23474             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23475         }
23476         
23477         if(this.valueField){
23478             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23479         }
23480         if(this.hiddenField){
23481             this.hiddenField.value = vv;
23482             
23483             this.lastSelectionText = dv;
23484             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23485             this.value = vv;
23486             return;
23487         }
23488         // no hidden field.. - we store the value in 'value', but still display
23489         // display field!!!!
23490         this.lastSelectionText = dv;
23491         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23492         this.value = vv;
23493         
23494         
23495     },
23496     // private
23497     reset : function(){
23498         // overridden so that last data is reset..
23499         this.setValue(this.originalValue);
23500         this.clearInvalid();
23501         this.lastData = false;
23502     },
23503     // private
23504     findRecord : function(prop, value){
23505         var record;
23506         if(this.store.getCount() > 0){
23507             this.store.each(function(r){
23508                 if(r.data[prop] == value){
23509                     record = r;
23510                     return false;
23511                 }
23512                 return true;
23513             });
23514         }
23515         return record;
23516     },
23517     
23518     getName: function()
23519     {
23520         // returns hidden if it's set..
23521         if (!this.rendered) {return ''};
23522         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
23523         
23524     },
23525     // private
23526     onViewMove : function(e, t){
23527         this.inKeyMode = false;
23528     },
23529
23530     // private
23531     onViewOver : function(e, t){
23532         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23533             return;
23534         }
23535         var item = this.view.findItemFromChild(t);
23536         if(item){
23537             var index = this.view.indexOf(item);
23538             this.select(index, false);
23539         }
23540     },
23541
23542     // private
23543     onViewClick : function(doFocus)
23544     {
23545         var index = this.view.getSelectedIndexes()[0];
23546         var r = this.store.getAt(index);
23547         if(r){
23548             this.onSelect(r, index);
23549         }
23550         if(doFocus !== false && !this.blockFocus){
23551             this.el.focus();
23552         }
23553     },
23554
23555     // private
23556     restrictHeight : function(){
23557         this.innerList.dom.style.height = '';
23558         var inner = this.innerList.dom;
23559         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23560         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23561         this.list.beginUpdate();
23562         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23563         this.list.alignTo(this.el, this.listAlign);
23564         this.list.endUpdate();
23565     },
23566
23567     // private
23568     onEmptyResults : function(){
23569         this.collapse();
23570     },
23571
23572     /**
23573      * Returns true if the dropdown list is expanded, else false.
23574      */
23575     isExpanded : function(){
23576         return this.list.isVisible();
23577     },
23578
23579     /**
23580      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23581      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23582      * @param {String} value The data value of the item to select
23583      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23584      * selected item if it is not currently in view (defaults to true)
23585      * @return {Boolean} True if the value matched an item in the list, else false
23586      */
23587     selectByValue : function(v, scrollIntoView){
23588         if(v !== undefined && v !== null){
23589             var r = this.findRecord(this.valueField || this.displayField, v);
23590             if(r){
23591                 this.select(this.store.indexOf(r), scrollIntoView);
23592                 return true;
23593             }
23594         }
23595         return false;
23596     },
23597
23598     /**
23599      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23600      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23601      * @param {Number} index The zero-based index of the list item to select
23602      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23603      * selected item if it is not currently in view (defaults to true)
23604      */
23605     select : function(index, scrollIntoView){
23606         this.selectedIndex = index;
23607         this.view.select(index);
23608         if(scrollIntoView !== false){
23609             var el = this.view.getNode(index);
23610             if(el){
23611                 this.innerList.scrollChildIntoView(el, false);
23612             }
23613         }
23614     },
23615
23616     // private
23617     selectNext : function(){
23618         var ct = this.store.getCount();
23619         if(ct > 0){
23620             if(this.selectedIndex == -1){
23621                 this.select(0);
23622             }else if(this.selectedIndex < ct-1){
23623                 this.select(this.selectedIndex+1);
23624             }
23625         }
23626     },
23627
23628     // private
23629     selectPrev : function(){
23630         var ct = this.store.getCount();
23631         if(ct > 0){
23632             if(this.selectedIndex == -1){
23633                 this.select(0);
23634             }else if(this.selectedIndex != 0){
23635                 this.select(this.selectedIndex-1);
23636             }
23637         }
23638     },
23639
23640     // private
23641     onKeyUp : function(e){
23642         if(this.editable !== false && !e.isSpecialKey()){
23643             this.lastKey = e.getKey();
23644             this.dqTask.delay(this.queryDelay);
23645         }
23646     },
23647
23648     // private
23649     validateBlur : function(){
23650         return !this.list || !this.list.isVisible();   
23651     },
23652
23653     // private
23654     initQuery : function(){
23655         this.doQuery(this.getRawValue());
23656     },
23657
23658     // private
23659     doForce : function(){
23660         if(this.el.dom.value.length > 0){
23661             this.el.dom.value =
23662                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23663             this.applyEmptyText();
23664         }
23665     },
23666
23667     /**
23668      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23669      * query allowing the query action to be canceled if needed.
23670      * @param {String} query The SQL query to execute
23671      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23672      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23673      * saved in the current store (defaults to false)
23674      */
23675     doQuery : function(q, forceAll){
23676         if(q === undefined || q === null){
23677             q = '';
23678         }
23679         var qe = {
23680             query: q,
23681             forceAll: forceAll,
23682             combo: this,
23683             cancel:false
23684         };
23685         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23686             return false;
23687         }
23688         q = qe.query;
23689         forceAll = qe.forceAll;
23690         if(forceAll === true || (q.length >= this.minChars)){
23691             if(this.lastQuery != q || this.alwaysQuery){
23692                 this.lastQuery = q;
23693                 if(this.mode == 'local'){
23694                     this.selectedIndex = -1;
23695                     if(forceAll){
23696                         this.store.clearFilter();
23697                     }else{
23698                         this.store.filter(this.displayField, q);
23699                     }
23700                     this.onLoad();
23701                 }else{
23702                     this.store.baseParams[this.queryParam] = q;
23703                     this.store.load({
23704                         params: this.getParams(q)
23705                     });
23706                     this.expand();
23707                 }
23708             }else{
23709                 this.selectedIndex = -1;
23710                 this.onLoad();   
23711             }
23712         }
23713     },
23714
23715     // private
23716     getParams : function(q){
23717         var p = {};
23718         //p[this.queryParam] = q;
23719         if(this.pageSize){
23720             p.start = 0;
23721             p.limit = this.pageSize;
23722         }
23723         return p;
23724     },
23725
23726     /**
23727      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23728      */
23729     collapse : function(){
23730         if(!this.isExpanded()){
23731             return;
23732         }
23733         this.list.hide();
23734         Roo.get(document).un('mousedown', this.collapseIf, this);
23735         Roo.get(document).un('mousewheel', this.collapseIf, this);
23736         if (!this.editable) {
23737             Roo.get(document).un('keydown', this.listKeyPress, this);
23738         }
23739         this.fireEvent('collapse', this);
23740     },
23741
23742     // private
23743     collapseIf : function(e){
23744         if(!e.within(this.wrap) && !e.within(this.list)){
23745             this.collapse();
23746         }
23747     },
23748
23749     /**
23750      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23751      */
23752     expand : function(){
23753         if(this.isExpanded() || !this.hasFocus){
23754             return;
23755         }
23756         this.list.alignTo(this.el, this.listAlign);
23757         this.list.show();
23758         Roo.get(document).on('mousedown', this.collapseIf, this);
23759         Roo.get(document).on('mousewheel', this.collapseIf, this);
23760         if (!this.editable) {
23761             Roo.get(document).on('keydown', this.listKeyPress, this);
23762         }
23763         
23764         this.fireEvent('expand', this);
23765     },
23766
23767     // private
23768     // Implements the default empty TriggerField.onTriggerClick function
23769     onTriggerClick : function(){
23770         if(this.disabled){
23771             return;
23772         }
23773         if(this.isExpanded()){
23774             this.collapse();
23775             if (!this.blockFocus) {
23776                 this.el.focus();
23777             }
23778             
23779         }else {
23780             this.hasFocus = true;
23781             if(this.triggerAction == 'all') {
23782                 this.doQuery(this.allQuery, true);
23783             } else {
23784                 this.doQuery(this.getRawValue());
23785             }
23786             if (!this.blockFocus) {
23787                 this.el.focus();
23788             }
23789         }
23790     },
23791     listKeyPress : function(e)
23792     {
23793         //Roo.log('listkeypress');
23794         // scroll to first matching element based on key pres..
23795         if (e.isSpecialKey()) {
23796             return false;
23797         }
23798         var k = String.fromCharCode(e.getKey()).toUpperCase();
23799         //Roo.log(k);
23800         var match  = false;
23801         var csel = this.view.getSelectedNodes();
23802         var cselitem = false;
23803         if (csel.length) {
23804             var ix = this.view.indexOf(csel[0]);
23805             cselitem  = this.store.getAt(ix);
23806             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
23807                 cselitem = false;
23808             }
23809             
23810         }
23811         
23812         this.store.each(function(v) { 
23813             if (cselitem) {
23814                 // start at existing selection.
23815                 if (cselitem.id == v.id) {
23816                     cselitem = false;
23817                 }
23818                 return;
23819             }
23820                 
23821             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
23822                 match = this.store.indexOf(v);
23823                 return false;
23824             }
23825         }, this);
23826         
23827         if (match === false) {
23828             return true; // no more action?
23829         }
23830         // scroll to?
23831         this.view.select(match);
23832         var sn = Roo.get(this.view.getSelectedNodes()[0])
23833         sn.scrollIntoView(sn.dom.parentNode, false);
23834     }
23835
23836     /** 
23837     * @cfg {Boolean} grow 
23838     * @hide 
23839     */
23840     /** 
23841     * @cfg {Number} growMin 
23842     * @hide 
23843     */
23844     /** 
23845     * @cfg {Number} growMax 
23846     * @hide 
23847     */
23848     /**
23849      * @hide
23850      * @method autoSize
23851      */
23852 });/*
23853  * Based on:
23854  * Ext JS Library 1.1.1
23855  * Copyright(c) 2006-2007, Ext JS, LLC.
23856  *
23857  * Originally Released Under LGPL - original licence link has changed is not relivant.
23858  *
23859  * Fork - LGPL
23860  * <script type="text/javascript">
23861  */
23862 /**
23863  * @class Roo.form.Checkbox
23864  * @extends Roo.form.Field
23865  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
23866  * @constructor
23867  * Creates a new Checkbox
23868  * @param {Object} config Configuration options
23869  */
23870 Roo.form.Checkbox = function(config){
23871     Roo.form.Checkbox.superclass.constructor.call(this, config);
23872     this.addEvents({
23873         /**
23874          * @event check
23875          * Fires when the checkbox is checked or unchecked.
23876              * @param {Roo.form.Checkbox} this This checkbox
23877              * @param {Boolean} checked The new checked value
23878              */
23879         check : true
23880     });
23881 };
23882
23883 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
23884     /**
23885      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
23886      */
23887     focusClass : undefined,
23888     /**
23889      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
23890      */
23891     fieldClass: "x-form-field",
23892     /**
23893      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
23894      */
23895     checked: false,
23896     /**
23897      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
23898      * {tag: "input", type: "checkbox", autocomplete: "off"})
23899      */
23900     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
23901     /**
23902      * @cfg {String} boxLabel The text that appears beside the checkbox
23903      */
23904     boxLabel : "",
23905     /**
23906      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
23907      */  
23908     inputValue : '1',
23909     /**
23910      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23911      */
23912      valueOff: '0', // value when not checked..
23913
23914     actionMode : 'viewEl', 
23915     //
23916     // private
23917     itemCls : 'x-menu-check-item x-form-item',
23918     groupClass : 'x-menu-group-item',
23919     inputType : 'hidden',
23920     
23921     
23922     inSetChecked: false, // check that we are not calling self...
23923     
23924     inputElement: false, // real input element?
23925     basedOn: false, // ????
23926     
23927     isFormField: true, // not sure where this is needed!!!!
23928
23929     onResize : function(){
23930         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
23931         if(!this.boxLabel){
23932             this.el.alignTo(this.wrap, 'c-c');
23933         }
23934     },
23935
23936     initEvents : function(){
23937         Roo.form.Checkbox.superclass.initEvents.call(this);
23938         this.el.on("click", this.onClick,  this);
23939         this.el.on("change", this.onClick,  this);
23940     },
23941
23942
23943     getResizeEl : function(){
23944         return this.wrap;
23945     },
23946
23947     getPositionEl : function(){
23948         return this.wrap;
23949     },
23950
23951     // private
23952     onRender : function(ct, position){
23953         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
23954         /*
23955         if(this.inputValue !== undefined){
23956             this.el.dom.value = this.inputValue;
23957         }
23958         */
23959         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
23960         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
23961         var viewEl = this.wrap.createChild({ 
23962             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
23963         this.viewEl = viewEl;   
23964         this.wrap.on('click', this.onClick,  this); 
23965         
23966         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
23967         this.el.on('propertychange', this.setFromHidden,  this);  //ie
23968         
23969         
23970         
23971         if(this.boxLabel){
23972             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
23973         //    viewEl.on('click', this.onClick,  this); 
23974         }
23975         //if(this.checked){
23976             this.setChecked(this.checked);
23977         //}else{
23978             //this.checked = this.el.dom;
23979         //}
23980
23981     },
23982
23983     // private
23984     initValue : Roo.emptyFn,
23985
23986     /**
23987      * Returns the checked state of the checkbox.
23988      * @return {Boolean} True if checked, else false
23989      */
23990     getValue : function(){
23991         if(this.el){
23992             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
23993         }
23994         return this.valueOff;
23995         
23996     },
23997
23998         // private
23999     onClick : function(){ 
24000         this.setChecked(!this.checked);
24001
24002         //if(this.el.dom.checked != this.checked){
24003         //    this.setValue(this.el.dom.checked);
24004        // }
24005     },
24006
24007     /**
24008      * Sets the checked state of the checkbox.
24009      * On is always based on a string comparison between inputValue and the param.
24010      * @param {Boolean/String} value - the value to set 
24011      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
24012      */
24013     setValue : function(v,suppressEvent){
24014         
24015         
24016         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
24017         //if(this.el && this.el.dom){
24018         //    this.el.dom.checked = this.checked;
24019         //    this.el.dom.defaultChecked = this.checked;
24020         //}
24021         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
24022         //this.fireEvent("check", this, this.checked);
24023     },
24024     // private..
24025     setChecked : function(state,suppressEvent)
24026     {
24027         if (this.inSetChecked) {
24028             this.checked = state;
24029             return;
24030         }
24031         
24032     
24033         if(this.wrap){
24034             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
24035         }
24036         this.checked = state;
24037         if(suppressEvent !== true){
24038             this.fireEvent('check', this, state);
24039         }
24040         this.inSetChecked = true;
24041         this.el.dom.value = state ? this.inputValue : this.valueOff;
24042         this.inSetChecked = false;
24043         
24044     },
24045     // handle setting of hidden value by some other method!!?!?
24046     setFromHidden: function()
24047     {
24048         if(!this.el){
24049             return;
24050         }
24051         //console.log("SET FROM HIDDEN");
24052         //alert('setFrom hidden');
24053         this.setValue(this.el.dom.value);
24054     },
24055     
24056     onDestroy : function()
24057     {
24058         if(this.viewEl){
24059             Roo.get(this.viewEl).remove();
24060         }
24061          
24062         Roo.form.Checkbox.superclass.onDestroy.call(this);
24063     }
24064
24065 });/*
24066  * Based on:
24067  * Ext JS Library 1.1.1
24068  * Copyright(c) 2006-2007, Ext JS, LLC.
24069  *
24070  * Originally Released Under LGPL - original licence link has changed is not relivant.
24071  *
24072  * Fork - LGPL
24073  * <script type="text/javascript">
24074  */
24075  
24076 /**
24077  * @class Roo.form.Radio
24078  * @extends Roo.form.Checkbox
24079  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
24080  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
24081  * @constructor
24082  * Creates a new Radio
24083  * @param {Object} config Configuration options
24084  */
24085 Roo.form.Radio = function(){
24086     Roo.form.Radio.superclass.constructor.apply(this, arguments);
24087 };
24088 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
24089     inputType: 'radio',
24090
24091     /**
24092      * If this radio is part of a group, it will return the selected value
24093      * @return {String}
24094      */
24095     getGroupValue : function(){
24096         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
24097     }
24098 });//<script type="text/javascript">
24099
24100 /*
24101  * Ext JS Library 1.1.1
24102  * Copyright(c) 2006-2007, Ext JS, LLC.
24103  * licensing@extjs.com
24104  * 
24105  * http://www.extjs.com/license
24106  */
24107  
24108  /*
24109   * 
24110   * Known bugs:
24111   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
24112   * - IE ? - no idea how much works there.
24113   * 
24114   * 
24115   * 
24116   */
24117  
24118
24119 /**
24120  * @class Ext.form.HtmlEditor
24121  * @extends Ext.form.Field
24122  * Provides a lightweight HTML Editor component.
24123  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
24124  * 
24125  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
24126  * supported by this editor.</b><br/><br/>
24127  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
24128  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24129  */
24130 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
24131       /**
24132      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
24133      */
24134     toolbars : false,
24135     /**
24136      * @cfg {String} createLinkText The default text for the create link prompt
24137      */
24138     createLinkText : 'Please enter the URL for the link:',
24139     /**
24140      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
24141      */
24142     defaultLinkValue : 'http:/'+'/',
24143    
24144      /**
24145      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24146      *                        Roo.resizable.
24147      */
24148     resizable : false,
24149      /**
24150      * @cfg {Number} height (in pixels)
24151      */   
24152     height: 300,
24153    /**
24154      * @cfg {Number} width (in pixels)
24155      */   
24156     width: 500,
24157     
24158     /**
24159      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24160      * 
24161      */
24162     stylesheets: false,
24163     
24164     // id of frame..
24165     frameId: false,
24166     
24167     // private properties
24168     validationEvent : false,
24169     deferHeight: true,
24170     initialized : false,
24171     activated : false,
24172     sourceEditMode : false,
24173     onFocus : Roo.emptyFn,
24174     iframePad:3,
24175     hideMode:'offsets',
24176     
24177     defaultAutoCreate : { // modified by initCompnoent..
24178         tag: "textarea",
24179         style:"width:500px;height:300px;",
24180         autocomplete: "off"
24181     },
24182
24183     // private
24184     initComponent : function(){
24185         this.addEvents({
24186             /**
24187              * @event initialize
24188              * Fires when the editor is fully initialized (including the iframe)
24189              * @param {HtmlEditor} this
24190              */
24191             initialize: true,
24192             /**
24193              * @event activate
24194              * Fires when the editor is first receives the focus. Any insertion must wait
24195              * until after this event.
24196              * @param {HtmlEditor} this
24197              */
24198             activate: true,
24199              /**
24200              * @event beforesync
24201              * Fires before the textarea is updated with content from the editor iframe. Return false
24202              * to cancel the sync.
24203              * @param {HtmlEditor} this
24204              * @param {String} html
24205              */
24206             beforesync: true,
24207              /**
24208              * @event beforepush
24209              * Fires before the iframe editor is updated with content from the textarea. Return false
24210              * to cancel the push.
24211              * @param {HtmlEditor} this
24212              * @param {String} html
24213              */
24214             beforepush: true,
24215              /**
24216              * @event sync
24217              * Fires when the textarea is updated with content from the editor iframe.
24218              * @param {HtmlEditor} this
24219              * @param {String} html
24220              */
24221             sync: true,
24222              /**
24223              * @event push
24224              * Fires when the iframe editor is updated with content from the textarea.
24225              * @param {HtmlEditor} this
24226              * @param {String} html
24227              */
24228             push: true,
24229              /**
24230              * @event editmodechange
24231              * Fires when the editor switches edit modes
24232              * @param {HtmlEditor} this
24233              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
24234              */
24235             editmodechange: true,
24236             /**
24237              * @event editorevent
24238              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24239              * @param {HtmlEditor} this
24240              */
24241             editorevent: true
24242         });
24243         this.defaultAutoCreate =  {
24244             tag: "textarea",
24245             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
24246             autocomplete: "off"
24247         };
24248     },
24249
24250     /**
24251      * Protected method that will not generally be called directly. It
24252      * is called when the editor creates its toolbar. Override this method if you need to
24253      * add custom toolbar buttons.
24254      * @param {HtmlEditor} editor
24255      */
24256     createToolbar : function(editor){
24257         if (!editor.toolbars || !editor.toolbars.length) {
24258             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
24259         }
24260         
24261         for (var i =0 ; i < editor.toolbars.length;i++) {
24262             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
24263             editor.toolbars[i].init(editor);
24264         }
24265          
24266         
24267     },
24268
24269     /**
24270      * Protected method that will not generally be called directly. It
24271      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24272      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24273      */
24274     getDocMarkup : function(){
24275         // body styles..
24276         var st = '';
24277         if (this.stylesheets === false) {
24278             
24279             Roo.get(document.head).select('style').each(function(node) {
24280                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24281             });
24282             
24283             Roo.get(document.head).select('link').each(function(node) { 
24284                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24285             });
24286             
24287         } else if (!this.stylesheets.length) {
24288                 // simple..
24289                 st = '<style type="text/css">' +
24290                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24291                    '</style>';
24292         } else {
24293             Roo.each(this.stylesheets, function(s) {
24294                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
24295             });
24296             
24297         }
24298         
24299         return '<html><head>' + st  +
24300             //<style type="text/css">' +
24301             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24302             //'</style>' +
24303             ' </head><body></body></html>';
24304     },
24305
24306     // private
24307     onRender : function(ct, position)
24308     {
24309         var _t = this;
24310         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
24311         this.el.dom.style.border = '0 none';
24312         this.el.dom.setAttribute('tabIndex', -1);
24313         this.el.addClass('x-hidden');
24314         if(Roo.isIE){ // fix IE 1px bogus margin
24315             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24316         }
24317         this.wrap = this.el.wrap({
24318             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
24319         });
24320         
24321         if (this.resizable) {
24322             this.resizeEl = new Roo.Resizable(this.wrap, {
24323                 pinned : true,
24324                 wrap: true,
24325                 dynamic : true,
24326                 minHeight : this.height,
24327                 height: this.height,
24328                 handles : this.resizable,
24329                 width: this.width,
24330                 listeners : {
24331                     resize : function(r, w, h) {
24332                         _t.onResize(w,h); // -something
24333                     }
24334                 }
24335             });
24336             
24337         }
24338
24339         this.frameId = Roo.id();
24340         
24341         this.createToolbar(this);
24342         
24343       
24344         
24345         var iframe = this.wrap.createChild({
24346             tag: 'iframe',
24347             id: this.frameId,
24348             name: this.frameId,
24349             frameBorder : 'no',
24350             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24351         }, this.el
24352         );
24353         
24354        // console.log(iframe);
24355         //this.wrap.dom.appendChild(iframe);
24356
24357         this.iframe = iframe.dom;
24358
24359          this.assignDocWin();
24360         
24361         this.doc.designMode = 'on';
24362        
24363         this.doc.open();
24364         this.doc.write(this.getDocMarkup());
24365         this.doc.close();
24366
24367         
24368         var task = { // must defer to wait for browser to be ready
24369             run : function(){
24370                 //console.log("run task?" + this.doc.readyState);
24371                 this.assignDocWin();
24372                 if(this.doc.body || this.doc.readyState == 'complete'){
24373                     try {
24374                         this.doc.designMode="on";
24375                     } catch (e) {
24376                         return;
24377                     }
24378                     Roo.TaskMgr.stop(task);
24379                     this.initEditor.defer(10, this);
24380                 }
24381             },
24382             interval : 10,
24383             duration:10000,
24384             scope: this
24385         };
24386         Roo.TaskMgr.start(task);
24387
24388         if(!this.width){
24389             this.setSize(this.wrap.getSize());
24390         }
24391         if (this.resizeEl) {
24392             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
24393             // should trigger onReize..
24394         }
24395     },
24396
24397     // private
24398     onResize : function(w, h)
24399     {
24400         //Roo.log('resize: ' +w + ',' + h );
24401         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
24402         if(this.el && this.iframe){
24403             if(typeof w == 'number'){
24404                 var aw = w - this.wrap.getFrameWidth('lr');
24405                 this.el.setWidth(this.adjustWidth('textarea', aw));
24406                 this.iframe.style.width = aw + 'px';
24407             }
24408             if(typeof h == 'number'){
24409                 var tbh = 0;
24410                 for (var i =0; i < this.toolbars.length;i++) {
24411                     // fixme - ask toolbars for heights?
24412                     tbh += this.toolbars[i].tb.el.getHeight();
24413                     if (this.toolbars[i].footer) {
24414                         tbh += this.toolbars[i].footer.el.getHeight();
24415                     }
24416                 }
24417                 
24418                 
24419                 
24420                 
24421                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24422                 ah -= 5; // knock a few pixes off for look..
24423                 this.el.setHeight(this.adjustWidth('textarea', ah));
24424                 this.iframe.style.height = ah + 'px';
24425                 if(this.doc){
24426                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
24427                 }
24428             }
24429         }
24430     },
24431
24432     /**
24433      * Toggles the editor between standard and source edit mode.
24434      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24435      */
24436     toggleSourceEdit : function(sourceEditMode){
24437         
24438         this.sourceEditMode = sourceEditMode === true;
24439         
24440         if(this.sourceEditMode){
24441           
24442             this.syncValue();
24443             this.iframe.className = 'x-hidden';
24444             this.el.removeClass('x-hidden');
24445             this.el.dom.removeAttribute('tabIndex');
24446             this.el.focus();
24447         }else{
24448              
24449             this.pushValue();
24450             this.iframe.className = '';
24451             this.el.addClass('x-hidden');
24452             this.el.dom.setAttribute('tabIndex', -1);
24453             this.deferFocus();
24454         }
24455         this.setSize(this.wrap.getSize());
24456         this.fireEvent('editmodechange', this, this.sourceEditMode);
24457     },
24458
24459     // private used internally
24460     createLink : function(){
24461         var url = prompt(this.createLinkText, this.defaultLinkValue);
24462         if(url && url != 'http:/'+'/'){
24463             this.relayCmd('createlink', url);
24464         }
24465     },
24466
24467     // private (for BoxComponent)
24468     adjustSize : Roo.BoxComponent.prototype.adjustSize,
24469
24470     // private (for BoxComponent)
24471     getResizeEl : function(){
24472         return this.wrap;
24473     },
24474
24475     // private (for BoxComponent)
24476     getPositionEl : function(){
24477         return this.wrap;
24478     },
24479
24480     // private
24481     initEvents : function(){
24482         this.originalValue = this.getValue();
24483     },
24484
24485     /**
24486      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24487      * @method
24488      */
24489     markInvalid : Roo.emptyFn,
24490     /**
24491      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24492      * @method
24493      */
24494     clearInvalid : Roo.emptyFn,
24495
24496     setValue : function(v){
24497         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
24498         this.pushValue();
24499     },
24500
24501     /**
24502      * Protected method that will not generally be called directly. If you need/want
24503      * custom HTML cleanup, this is the method you should override.
24504      * @param {String} html The HTML to be cleaned
24505      * return {String} The cleaned HTML
24506      */
24507     cleanHtml : function(html){
24508         html = String(html);
24509         if(html.length > 5){
24510             if(Roo.isSafari){ // strip safari nonsense
24511                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24512             }
24513         }
24514         if(html == '&nbsp;'){
24515             html = '';
24516         }
24517         return html;
24518     },
24519
24520     /**
24521      * Protected method that will not generally be called directly. Syncs the contents
24522      * of the editor iframe with the textarea.
24523      */
24524     syncValue : function(){
24525         if(this.initialized){
24526             var bd = (this.doc.body || this.doc.documentElement);
24527             //this.cleanUpPaste();
24528             var html = bd.innerHTML;
24529             if(Roo.isSafari){
24530                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24531                 var m = bs.match(/text-align:(.*?);/i);
24532                 if(m && m[1]){
24533                     html = '<div style="'+m[0]+'">' + html + '</div>';
24534                 }
24535             }
24536             html = this.cleanHtml(html);
24537             if(this.fireEvent('beforesync', this, html) !== false){
24538                 this.el.dom.value = html;
24539                 this.fireEvent('sync', this, html);
24540             }
24541         }
24542     },
24543
24544     /**
24545      * Protected method that will not generally be called directly. Pushes the value of the textarea
24546      * into the iframe editor.
24547      */
24548     pushValue : function(){
24549         if(this.initialized){
24550             var v = this.el.dom.value;
24551             if(v.length < 1){
24552                 v = '&#160;';
24553             }
24554             
24555             if(this.fireEvent('beforepush', this, v) !== false){
24556                 var d = (this.doc.body || this.doc.documentElement);
24557                 d.innerHTML = v;
24558                 this.cleanUpPaste();
24559                 this.el.dom.value = d.innerHTML;
24560                 this.fireEvent('push', this, v);
24561             }
24562         }
24563     },
24564
24565     // private
24566     deferFocus : function(){
24567         this.focus.defer(10, this);
24568     },
24569
24570     // doc'ed in Field
24571     focus : function(){
24572         if(this.win && !this.sourceEditMode){
24573             this.win.focus();
24574         }else{
24575             this.el.focus();
24576         }
24577     },
24578     
24579     assignDocWin: function()
24580     {
24581         var iframe = this.iframe;
24582         
24583          if(Roo.isIE){
24584             this.doc = iframe.contentWindow.document;
24585             this.win = iframe.contentWindow;
24586         } else {
24587             if (!Roo.get(this.frameId)) {
24588                 return;
24589             }
24590             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24591             this.win = Roo.get(this.frameId).dom.contentWindow;
24592         }
24593     },
24594     
24595     // private
24596     initEditor : function(){
24597         //console.log("INIT EDITOR");
24598         this.assignDocWin();
24599         
24600         
24601         
24602         this.doc.designMode="on";
24603         this.doc.open();
24604         this.doc.write(this.getDocMarkup());
24605         this.doc.close();
24606         
24607         var dbody = (this.doc.body || this.doc.documentElement);
24608         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24609         // this copies styles from the containing element into thsi one..
24610         // not sure why we need all of this..
24611         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24612         ss['background-attachment'] = 'fixed'; // w3c
24613         dbody.bgProperties = 'fixed'; // ie
24614         Roo.DomHelper.applyStyles(dbody, ss);
24615         Roo.EventManager.on(this.doc, {
24616             //'mousedown': this.onEditorEvent,
24617             'mouseup': this.onEditorEvent,
24618             'dblclick': this.onEditorEvent,
24619             'click': this.onEditorEvent,
24620             'keyup': this.onEditorEvent,
24621             buffer:100,
24622             scope: this
24623         });
24624         if(Roo.isGecko){
24625             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24626         }
24627         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24628             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24629         }
24630         this.initialized = true;
24631
24632         this.fireEvent('initialize', this);
24633         this.pushValue();
24634     },
24635
24636     // private
24637     onDestroy : function(){
24638         
24639         
24640         
24641         if(this.rendered){
24642             
24643             for (var i =0; i < this.toolbars.length;i++) {
24644                 // fixme - ask toolbars for heights?
24645                 this.toolbars[i].onDestroy();
24646             }
24647             
24648             this.wrap.dom.innerHTML = '';
24649             this.wrap.remove();
24650         }
24651     },
24652
24653     // private
24654     onFirstFocus : function(){
24655         
24656         this.assignDocWin();
24657         
24658         
24659         this.activated = true;
24660         for (var i =0; i < this.toolbars.length;i++) {
24661             this.toolbars[i].onFirstFocus();
24662         }
24663        
24664         if(Roo.isGecko){ // prevent silly gecko errors
24665             this.win.focus();
24666             var s = this.win.getSelection();
24667             if(!s.focusNode || s.focusNode.nodeType != 3){
24668                 var r = s.getRangeAt(0);
24669                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24670                 r.collapse(true);
24671                 this.deferFocus();
24672             }
24673             try{
24674                 this.execCmd('useCSS', true);
24675                 this.execCmd('styleWithCSS', false);
24676             }catch(e){}
24677         }
24678         this.fireEvent('activate', this);
24679     },
24680
24681     // private
24682     adjustFont: function(btn){
24683         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24684         //if(Roo.isSafari){ // safari
24685         //    adjust *= 2;
24686        // }
24687         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24688         if(Roo.isSafari){ // safari
24689             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24690             v =  (v < 10) ? 10 : v;
24691             v =  (v > 48) ? 48 : v;
24692             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24693             
24694         }
24695         
24696         
24697         v = Math.max(1, v+adjust);
24698         
24699         this.execCmd('FontSize', v  );
24700     },
24701
24702     onEditorEvent : function(e){
24703         this.fireEvent('editorevent', this, e);
24704       //  this.updateToolbar();
24705         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24706     },
24707
24708     insertTag : function(tg)
24709     {
24710         // could be a bit smarter... -> wrap the current selected tRoo..
24711         
24712         this.execCmd("formatblock",   tg);
24713         
24714     },
24715     
24716     insertText : function(txt)
24717     {
24718         
24719         
24720         range = this.createRange();
24721         range.deleteContents();
24722                //alert(Sender.getAttribute('label'));
24723                
24724         range.insertNode(this.doc.createTextNode(txt));
24725     } ,
24726     
24727     // private
24728     relayBtnCmd : function(btn){
24729         this.relayCmd(btn.cmd);
24730     },
24731
24732     /**
24733      * Executes a Midas editor command on the editor document and performs necessary focus and
24734      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24735      * @param {String} cmd The Midas command
24736      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24737      */
24738     relayCmd : function(cmd, value){
24739         this.win.focus();
24740         this.execCmd(cmd, value);
24741         this.fireEvent('editorevent', this);
24742         //this.updateToolbar();
24743         this.deferFocus();
24744     },
24745
24746     /**
24747      * Executes a Midas editor command directly on the editor document.
24748      * For visual commands, you should use {@link #relayCmd} instead.
24749      * <b>This should only be called after the editor is initialized.</b>
24750      * @param {String} cmd The Midas command
24751      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24752      */
24753     execCmd : function(cmd, value){
24754         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24755         this.syncValue();
24756     },
24757
24758    
24759     /**
24760      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24761      * to insert tRoo.
24762      * @param {String} text
24763      */
24764     insertAtCursor : function(text){
24765         if(!this.activated){
24766             return;
24767         }
24768         if(Roo.isIE){
24769             this.win.focus();
24770             var r = this.doc.selection.createRange();
24771             if(r){
24772                 r.collapse(true);
24773                 r.pasteHTML(text);
24774                 this.syncValue();
24775                 this.deferFocus();
24776             }
24777         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24778             this.win.focus();
24779             this.execCmd('InsertHTML', text);
24780             this.deferFocus();
24781         }
24782     },
24783  // private
24784     mozKeyPress : function(e){
24785         if(e.ctrlKey){
24786             var c = e.getCharCode(), cmd;
24787           
24788             if(c > 0){
24789                 c = String.fromCharCode(c).toLowerCase();
24790                 switch(c){
24791                     case 'b':
24792                         cmd = 'bold';
24793                     break;
24794                     case 'i':
24795                         cmd = 'italic';
24796                     break;
24797                     case 'u':
24798                         cmd = 'underline';
24799                         break;
24800                     case 'v':
24801                         this.cleanUpPaste.defer(100, this);
24802                         return;
24803                     break;
24804                 }
24805                 if(cmd){
24806                     this.win.focus();
24807                     this.execCmd(cmd);
24808                     this.deferFocus();
24809                     e.preventDefault();
24810                 }
24811                 
24812             }
24813         }
24814     },
24815
24816     // private
24817     fixKeys : function(){ // load time branching for fastest keydown performance
24818         if(Roo.isIE){
24819             return function(e){
24820                 var k = e.getKey(), r;
24821                 if(k == e.TAB){
24822                     e.stopEvent();
24823                     r = this.doc.selection.createRange();
24824                     if(r){
24825                         r.collapse(true);
24826                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24827                         this.deferFocus();
24828                     }
24829                     return;
24830                 }
24831                 
24832                 if(k == e.ENTER){
24833                     r = this.doc.selection.createRange();
24834                     if(r){
24835                         var target = r.parentElement();
24836                         if(!target || target.tagName.toLowerCase() != 'li'){
24837                             e.stopEvent();
24838                             r.pasteHTML('<br />');
24839                             r.collapse(false);
24840                             r.select();
24841                         }
24842                     }
24843                 }
24844                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24845                     this.cleanUpPaste.defer(100, this);
24846                     return;
24847                 }
24848                 
24849                 
24850             };
24851         }else if(Roo.isOpera){
24852             return function(e){
24853                 var k = e.getKey();
24854                 if(k == e.TAB){
24855                     e.stopEvent();
24856                     this.win.focus();
24857                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24858                     this.deferFocus();
24859                 }
24860                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24861                     this.cleanUpPaste.defer(100, this);
24862                     return;
24863                 }
24864                 
24865             };
24866         }else if(Roo.isSafari){
24867             return function(e){
24868                 var k = e.getKey();
24869                 
24870                 if(k == e.TAB){
24871                     e.stopEvent();
24872                     this.execCmd('InsertText','\t');
24873                     this.deferFocus();
24874                     return;
24875                 }
24876                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24877                     this.cleanUpPaste.defer(100, this);
24878                     return;
24879                 }
24880                 
24881              };
24882         }
24883     }(),
24884     
24885     getAllAncestors: function()
24886     {
24887         var p = this.getSelectedNode();
24888         var a = [];
24889         if (!p) {
24890             a.push(p); // push blank onto stack..
24891             p = this.getParentElement();
24892         }
24893         
24894         
24895         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24896             a.push(p);
24897             p = p.parentNode;
24898         }
24899         a.push(this.doc.body);
24900         return a;
24901     },
24902     lastSel : false,
24903     lastSelNode : false,
24904     
24905     
24906     getSelection : function() 
24907     {
24908         this.assignDocWin();
24909         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24910     },
24911     
24912     getSelectedNode: function() 
24913     {
24914         // this may only work on Gecko!!!
24915         
24916         // should we cache this!!!!
24917         
24918         
24919         
24920          
24921         var range = this.createRange(this.getSelection()).cloneRange();
24922         
24923         if (Roo.isIE) {
24924             var parent = range.parentElement();
24925             while (true) {
24926                 var testRange = range.duplicate();
24927                 testRange.moveToElementText(parent);
24928                 if (testRange.inRange(range)) {
24929                     break;
24930                 }
24931                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24932                     break;
24933                 }
24934                 parent = parent.parentElement;
24935             }
24936             return parent;
24937         }
24938         
24939         // is ancestor a text element.
24940         var ac =  range.commonAncestorContainer;
24941         if (ac.nodeType == 3) {
24942             ac = ac.parentNode;
24943         }
24944         
24945         var ar = ac.childNodes;
24946          
24947         var nodes = [];
24948         var other_nodes = [];
24949         var has_other_nodes = false;
24950         for (var i=0;i<ar.length;i++) {
24951             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24952                 continue;
24953             }
24954             // fullly contained node.
24955             
24956             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24957                 nodes.push(ar[i]);
24958                 continue;
24959             }
24960             
24961             // probably selected..
24962             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24963                 other_nodes.push(ar[i]);
24964                 continue;
24965             }
24966             // outer..
24967             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24968                 continue;
24969             }
24970             
24971             
24972             has_other_nodes = true;
24973         }
24974         if (!nodes.length && other_nodes.length) {
24975             nodes= other_nodes;
24976         }
24977         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24978             return false;
24979         }
24980         
24981         return nodes[0];
24982     },
24983     createRange: function(sel)
24984     {
24985         // this has strange effects when using with 
24986         // top toolbar - not sure if it's a great idea.
24987         //this.editor.contentWindow.focus();
24988         if (typeof sel != "undefined") {
24989             try {
24990                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24991             } catch(e) {
24992                 return this.doc.createRange();
24993             }
24994         } else {
24995             return this.doc.createRange();
24996         }
24997     },
24998     getParentElement: function()
24999     {
25000         
25001         this.assignDocWin();
25002         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25003         
25004         var range = this.createRange(sel);
25005          
25006         try {
25007             var p = range.commonAncestorContainer;
25008             while (p.nodeType == 3) { // text node
25009                 p = p.parentNode;
25010             }
25011             return p;
25012         } catch (e) {
25013             return null;
25014         }
25015     
25016     },
25017     /***
25018      *
25019      * Range intersection.. the hard stuff...
25020      *  '-1' = before
25021      *  '0' = hits..
25022      *  '1' = after.
25023      *         [ -- selected range --- ]
25024      *   [fail]                        [fail]
25025      *
25026      *    basically..
25027      *      if end is before start or  hits it. fail.
25028      *      if start is after end or hits it fail.
25029      *
25030      *   if either hits (but other is outside. - then it's not 
25031      *   
25032      *    
25033      **/
25034     
25035     
25036     // @see http://www.thismuchiknow.co.uk/?p=64.
25037     rangeIntersectsNode : function(range, node)
25038     {
25039         var nodeRange = node.ownerDocument.createRange();
25040         try {
25041             nodeRange.selectNode(node);
25042         } catch (e) {
25043             nodeRange.selectNodeContents(node);
25044         }
25045     
25046         var rangeStartRange = range.cloneRange();
25047         rangeStartRange.collapse(true);
25048     
25049         var rangeEndRange = range.cloneRange();
25050         rangeEndRange.collapse(false);
25051     
25052         var nodeStartRange = nodeRange.cloneRange();
25053         nodeStartRange.collapse(true);
25054     
25055         var nodeEndRange = nodeRange.cloneRange();
25056         nodeEndRange.collapse(false);
25057     
25058         return rangeStartRange.compareBoundaryPoints(
25059                  Range.START_TO_START, nodeEndRange) == -1 &&
25060                rangeEndRange.compareBoundaryPoints(
25061                  Range.START_TO_START, nodeStartRange) == 1;
25062         
25063          
25064     },
25065     rangeCompareNode : function(range, node)
25066     {
25067         var nodeRange = node.ownerDocument.createRange();
25068         try {
25069             nodeRange.selectNode(node);
25070         } catch (e) {
25071             nodeRange.selectNodeContents(node);
25072         }
25073         
25074         
25075         range.collapse(true);
25076     
25077         nodeRange.collapse(true);
25078      
25079         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25080         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25081          
25082         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25083         
25084         var nodeIsBefore   =  ss == 1;
25085         var nodeIsAfter    = ee == -1;
25086         
25087         if (nodeIsBefore && nodeIsAfter)
25088             return 0; // outer
25089         if (!nodeIsBefore && nodeIsAfter)
25090             return 1; //right trailed.
25091         
25092         if (nodeIsBefore && !nodeIsAfter)
25093             return 2;  // left trailed.
25094         // fully contined.
25095         return 3;
25096     },
25097
25098     // private? - in a new class?
25099     cleanUpPaste :  function()
25100     {
25101         // cleans up the whole document..
25102          Roo.log('cleanuppaste');
25103         this.cleanUpChildren(this.doc.body);
25104         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25105         if (clean != this.doc.body.innerHTML) {
25106             this.doc.body.innerHTML = clean;
25107         }
25108         
25109     },
25110     
25111     cleanWordChars : function(input) {
25112         var he = Roo.form.HtmlEditor;
25113     
25114         var output = input;
25115         Roo.each(he.swapCodes, function(sw) { 
25116         
25117             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25118             output = output.replace(swapper, sw[1]);
25119         });
25120         return output;
25121     },
25122     
25123     
25124     cleanUpChildren : function (n)
25125     {
25126         if (!n.childNodes.length) {
25127             return;
25128         }
25129         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25130            this.cleanUpChild(n.childNodes[i]);
25131         }
25132     },
25133     
25134     
25135         
25136     
25137     cleanUpChild : function (node)
25138     {
25139         //console.log(node);
25140         if (node.nodeName == "#text") {
25141             // clean up silly Windows -- stuff?
25142             return; 
25143         }
25144         if (node.nodeName == "#comment") {
25145             node.parentNode.removeChild(node);
25146             // clean up silly Windows -- stuff?
25147             return; 
25148         }
25149         
25150         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
25151             // remove node.
25152             node.parentNode.removeChild(node);
25153             return;
25154             
25155         }
25156         
25157         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
25158         
25159         // remove <a name=....> as rendering on yahoo mailer is bored with this.
25160         
25161         if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25162             remove_keep_children = true;
25163         }
25164         
25165         if (remove_keep_children) {
25166             this.cleanUpChildren(node);
25167             // inserts everything just before this node...
25168             while (node.childNodes.length) {
25169                 var cn = node.childNodes[0];
25170                 node.removeChild(cn);
25171                 node.parentNode.insertBefore(cn, node);
25172             }
25173             node.parentNode.removeChild(node);
25174             return;
25175         }
25176         
25177         if (!node.attributes || !node.attributes.length) {
25178             this.cleanUpChildren(node);
25179             return;
25180         }
25181         
25182         function cleanAttr(n,v)
25183         {
25184             
25185             if (v.match(/^\./) || v.match(/^\//)) {
25186                 return;
25187             }
25188             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
25189                 return;
25190             }
25191             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
25192             node.removeAttribute(n);
25193             
25194         }
25195         
25196         function cleanStyle(n,v)
25197         {
25198             if (v.match(/expression/)) { //XSS?? should we even bother..
25199                 node.removeAttribute(n);
25200                 return;
25201             }
25202             
25203             
25204             var parts = v.split(/;/);
25205             Roo.each(parts, function(p) {
25206                 p = p.replace(/\s+/g,'');
25207                 if (!p.length) {
25208                     return true;
25209                 }
25210                 var l = p.split(':').shift().replace(/\s+/g,'');
25211                 
25212                 // only allow 'c whitelisted system attributes'
25213                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
25214                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
25215                     node.removeAttribute(n);
25216                     return false;
25217                 }
25218                 return true;
25219             });
25220             
25221             
25222         }
25223         
25224         
25225         for (var i = node.attributes.length-1; i > -1 ; i--) {
25226             var a = node.attributes[i];
25227             //console.log(a);
25228             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
25229                 node.removeAttribute(a.name);
25230                 return;
25231             }
25232             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
25233                 cleanAttr(a.name,a.value); // fixme..
25234                 return;
25235             }
25236             if (a.name == 'style') {
25237                 cleanStyle(a.name,a.value);
25238             }
25239             /// clean up MS crap..
25240             // tecnically this should be a list of valid class'es..
25241             
25242             
25243             if (a.name == 'class') {
25244                 if (a.value.match(/^Mso/)) {
25245                     node.className = '';
25246                 }
25247                 
25248                 if (a.value.match(/body/)) {
25249                     node.className = '';
25250                 }
25251             }
25252             
25253             // style cleanup!?
25254             // class cleanup?
25255             
25256         }
25257         
25258         
25259         this.cleanUpChildren(node);
25260         
25261         
25262     }
25263     
25264     
25265     // hide stuff that is not compatible
25266     /**
25267      * @event blur
25268      * @hide
25269      */
25270     /**
25271      * @event change
25272      * @hide
25273      */
25274     /**
25275      * @event focus
25276      * @hide
25277      */
25278     /**
25279      * @event specialkey
25280      * @hide
25281      */
25282     /**
25283      * @cfg {String} fieldClass @hide
25284      */
25285     /**
25286      * @cfg {String} focusClass @hide
25287      */
25288     /**
25289      * @cfg {String} autoCreate @hide
25290      */
25291     /**
25292      * @cfg {String} inputType @hide
25293      */
25294     /**
25295      * @cfg {String} invalidClass @hide
25296      */
25297     /**
25298      * @cfg {String} invalidText @hide
25299      */
25300     /**
25301      * @cfg {String} msgFx @hide
25302      */
25303     /**
25304      * @cfg {String} validateOnBlur @hide
25305      */
25306 });
25307
25308 Roo.form.HtmlEditor.white = [
25309         'area', 'br', 'img', 'input', 'hr', 'wbr',
25310         
25311        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25312        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25313        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25314        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25315        'table',   'ul',         'xmp', 
25316        
25317        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25318       'thead',   'tr', 
25319      
25320       'dir', 'menu', 'ol', 'ul', 'dl',
25321        
25322       'embed',  'object'
25323 ];
25324
25325
25326 Roo.form.HtmlEditor.black = [
25327     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25328         'applet', // 
25329         'base',   'basefont', 'bgsound', 'blink',  'body', 
25330         'frame',  'frameset', 'head',    'html',   'ilayer', 
25331         'iframe', 'layer',  'link',     'meta',    'object',   
25332         'script', 'style' ,'title',  'xml' // clean later..
25333 ];
25334 Roo.form.HtmlEditor.clean = [
25335     'script', 'style', 'title', 'xml'
25336 ];
25337 Roo.form.HtmlEditor.remove = [
25338     'font'
25339 ];
25340 // attributes..
25341
25342 Roo.form.HtmlEditor.ablack = [
25343     'on'
25344 ];
25345     
25346 Roo.form.HtmlEditor.aclean = [ 
25347     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25348 ];
25349
25350 // protocols..
25351 Roo.form.HtmlEditor.pwhite= [
25352         'http',  'https',  'mailto'
25353 ];
25354
25355 // white listed style attributes.
25356 Roo.form.HtmlEditor.cwhite= [
25357         'text-align',
25358         'font-size'
25359 ];
25360
25361
25362 Roo.form.HtmlEditor.swapCodes   =[ 
25363     [    8211, "--" ], 
25364     [    8212, "--" ], 
25365     [    8216,  "'" ],  
25366     [    8217, "'" ],  
25367     [    8220, '"' ],  
25368     [    8221, '"' ],  
25369     [    8226, "*" ],  
25370     [    8230, "..." ]
25371 ]; 
25372
25373     // <script type="text/javascript">
25374 /*
25375  * Based on
25376  * Ext JS Library 1.1.1
25377  * Copyright(c) 2006-2007, Ext JS, LLC.
25378  *  
25379  
25380  */
25381
25382 /**
25383  * @class Roo.form.HtmlEditorToolbar1
25384  * Basic Toolbar
25385  * 
25386  * Usage:
25387  *
25388  new Roo.form.HtmlEditor({
25389     ....
25390     toolbars : [
25391         new Roo.form.HtmlEditorToolbar1({
25392             disable : { fonts: 1 , format: 1, ..., ... , ...],
25393             btns : [ .... ]
25394         })
25395     }
25396      
25397  * 
25398  * @cfg {Object} disable List of elements to disable..
25399  * @cfg {Array} btns List of additional buttons.
25400  * 
25401  * 
25402  * NEEDS Extra CSS? 
25403  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25404  */
25405  
25406 Roo.form.HtmlEditor.ToolbarStandard = function(config)
25407 {
25408     
25409     Roo.apply(this, config);
25410     
25411     // default disabled, based on 'good practice'..
25412     this.disable = this.disable || {};
25413     Roo.applyIf(this.disable, {
25414         fontSize : true,
25415         colors : true,
25416         specialElements : true
25417     });
25418     
25419     
25420     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25421     // dont call parent... till later.
25422 }
25423
25424 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
25425     
25426     tb: false,
25427     
25428     rendered: false,
25429     
25430     editor : false,
25431     /**
25432      * @cfg {Object} disable  List of toolbar elements to disable
25433          
25434      */
25435     disable : false,
25436       /**
25437      * @cfg {Array} fontFamilies An array of available font families
25438      */
25439     fontFamilies : [
25440         'Arial',
25441         'Courier New',
25442         'Tahoma',
25443         'Times New Roman',
25444         'Verdana'
25445     ],
25446     
25447     specialChars : [
25448            "&#169;",
25449           "&#174;",     
25450           "&#8482;",    
25451           "&#163;" ,    
25452          // "&#8212;",    
25453           "&#8230;",    
25454           "&#247;" ,    
25455         //  "&#225;" ,     ?? a acute?
25456            "&#8364;"    , //Euro
25457        //   "&#8220;"    ,
25458         //  "&#8221;"    ,
25459         //  "&#8226;"    ,
25460           "&#176;"  //   , // degrees
25461
25462          // "&#233;"     , // e ecute
25463          // "&#250;"     , // u ecute?
25464     ],
25465     
25466     specialElements : [
25467         {
25468             text: "Insert Table",
25469             xtype: 'MenuItem',
25470             xns : Roo.Menu,
25471             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
25472                 
25473         },
25474         {    
25475             text: "Insert Image",
25476             xtype: 'MenuItem',
25477             xns : Roo.Menu,
25478             ihtml : '<img src="about:blank"/>'
25479             
25480         }
25481         
25482          
25483     ],
25484     
25485     
25486     inputElements : [ 
25487             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
25488             "input:submit", "input:button", "select", "textarea", "label" ],
25489     formats : [
25490         ["p"] ,  
25491         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
25492         ["pre"],[ "code"], 
25493         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
25494     ],
25495      /**
25496      * @cfg {String} defaultFont default font to use.
25497      */
25498     defaultFont: 'tahoma',
25499    
25500     fontSelect : false,
25501     
25502     
25503     formatCombo : false,
25504     
25505     init : function(editor)
25506     {
25507         this.editor = editor;
25508         
25509         
25510         var fid = editor.frameId;
25511         var etb = this;
25512         function btn(id, toggle, handler){
25513             var xid = fid + '-'+ id ;
25514             return {
25515                 id : xid,
25516                 cmd : id,
25517                 cls : 'x-btn-icon x-edit-'+id,
25518                 enableToggle:toggle !== false,
25519                 scope: editor, // was editor...
25520                 handler:handler||editor.relayBtnCmd,
25521                 clickEvent:'mousedown',
25522                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
25523                 tabIndex:-1
25524             };
25525         }
25526         
25527         
25528         
25529         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
25530         this.tb = tb;
25531          // stop form submits
25532         tb.el.on('click', function(e){
25533             e.preventDefault(); // what does this do?
25534         });
25535
25536         if(!this.disable.font && !Roo.isSafari){
25537             /* why no safari for fonts
25538             editor.fontSelect = tb.el.createChild({
25539                 tag:'select',
25540                 tabIndex: -1,
25541                 cls:'x-font-select',
25542                 html: editor.createFontOptions()
25543             });
25544             editor.fontSelect.on('change', function(){
25545                 var font = editor.fontSelect.dom.value;
25546                 editor.relayCmd('fontname', font);
25547                 editor.deferFocus();
25548             }, editor);
25549             tb.add(
25550                 editor.fontSelect.dom,
25551                 '-'
25552             );
25553             */
25554         };
25555         if(!this.disable.formats){
25556             this.formatCombo = new Roo.form.ComboBox({
25557                 store: new Roo.data.SimpleStore({
25558                     id : 'tag',
25559                     fields: ['tag'],
25560                     data : this.formats // from states.js
25561                 }),
25562                 blockFocus : true,
25563                 //autoCreate : {tag: "div",  size: "20"},
25564                 displayField:'tag',
25565                 typeAhead: false,
25566                 mode: 'local',
25567                 editable : false,
25568                 triggerAction: 'all',
25569                 emptyText:'Add tag',
25570                 selectOnFocus:true,
25571                 width:135,
25572                 listeners : {
25573                     'select': function(c, r, i) {
25574                         editor.insertTag(r.get('tag'));
25575                         editor.focus();
25576                     }
25577                 }
25578
25579             });
25580             tb.addField(this.formatCombo);
25581             
25582         }
25583         
25584         if(!this.disable.format){
25585             tb.add(
25586                 btn('bold'),
25587                 btn('italic'),
25588                 btn('underline')
25589             );
25590         };
25591         if(!this.disable.fontSize){
25592             tb.add(
25593                 '-',
25594                 
25595                 
25596                 btn('increasefontsize', false, editor.adjustFont),
25597                 btn('decreasefontsize', false, editor.adjustFont)
25598             );
25599         };
25600         
25601         
25602         if(!this.disable.colors){
25603             tb.add(
25604                 '-', {
25605                     id:editor.frameId +'-forecolor',
25606                     cls:'x-btn-icon x-edit-forecolor',
25607                     clickEvent:'mousedown',
25608                     tooltip: this.buttonTips['forecolor'] || undefined,
25609                     tabIndex:-1,
25610                     menu : new Roo.menu.ColorMenu({
25611                         allowReselect: true,
25612                         focus: Roo.emptyFn,
25613                         value:'000000',
25614                         plain:true,
25615                         selectHandler: function(cp, color){
25616                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
25617                             editor.deferFocus();
25618                         },
25619                         scope: editor,
25620                         clickEvent:'mousedown'
25621                     })
25622                 }, {
25623                     id:editor.frameId +'backcolor',
25624                     cls:'x-btn-icon x-edit-backcolor',
25625                     clickEvent:'mousedown',
25626                     tooltip: this.buttonTips['backcolor'] || undefined,
25627                     tabIndex:-1,
25628                     menu : new Roo.menu.ColorMenu({
25629                         focus: Roo.emptyFn,
25630                         value:'FFFFFF',
25631                         plain:true,
25632                         allowReselect: true,
25633                         selectHandler: function(cp, color){
25634                             if(Roo.isGecko){
25635                                 editor.execCmd('useCSS', false);
25636                                 editor.execCmd('hilitecolor', color);
25637                                 editor.execCmd('useCSS', true);
25638                                 editor.deferFocus();
25639                             }else{
25640                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
25641                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
25642                                 editor.deferFocus();
25643                             }
25644                         },
25645                         scope:editor,
25646                         clickEvent:'mousedown'
25647                     })
25648                 }
25649             );
25650         };
25651         // now add all the items...
25652         
25653
25654         if(!this.disable.alignments){
25655             tb.add(
25656                 '-',
25657                 btn('justifyleft'),
25658                 btn('justifycenter'),
25659                 btn('justifyright')
25660             );
25661         };
25662
25663         //if(!Roo.isSafari){
25664             if(!this.disable.links){
25665                 tb.add(
25666                     '-',
25667                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
25668                 );
25669             };
25670
25671             if(!this.disable.lists){
25672                 tb.add(
25673                     '-',
25674                     btn('insertorderedlist'),
25675                     btn('insertunorderedlist')
25676                 );
25677             }
25678             if(!this.disable.sourceEdit){
25679                 tb.add(
25680                     '-',
25681                     btn('sourceedit', true, function(btn){
25682                         this.toggleSourceEdit(btn.pressed);
25683                     })
25684                 );
25685             }
25686         //}
25687         
25688         var smenu = { };
25689         // special menu.. - needs to be tidied up..
25690         if (!this.disable.special) {
25691             smenu = {
25692                 text: "&#169;",
25693                 cls: 'x-edit-none',
25694                 
25695                 menu : {
25696                     items : []
25697                 }
25698             };
25699             for (var i =0; i < this.specialChars.length; i++) {
25700                 smenu.menu.items.push({
25701                     
25702                     html: this.specialChars[i],
25703                     handler: function(a,b) {
25704                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
25705                         
25706                     },
25707                     tabIndex:-1
25708                 });
25709             }
25710             
25711             
25712             tb.add(smenu);
25713             
25714             
25715         }
25716          
25717         if (!this.disable.specialElements) {
25718             var semenu = {
25719                 text: "Other;",
25720                 cls: 'x-edit-none',
25721                 menu : {
25722                     items : []
25723                 }
25724             };
25725             for (var i =0; i < this.specialElements.length; i++) {
25726                 semenu.menu.items.push(
25727                     Roo.apply({ 
25728                         handler: function(a,b) {
25729                             editor.insertAtCursor(this.ihtml);
25730                         }
25731                     }, this.specialElements[i])
25732                 );
25733                     
25734             }
25735             
25736             tb.add(semenu);
25737             
25738             
25739         }
25740          
25741         
25742         if (this.btns) {
25743             for(var i =0; i< this.btns.length;i++) {
25744                 var b = this.btns[i];
25745                 b.cls =  'x-edit-none';
25746                 b.scope = editor;
25747                 tb.add(b);
25748             }
25749         
25750         }
25751         
25752         
25753         
25754         // disable everything...
25755         
25756         this.tb.items.each(function(item){
25757            if(item.id != editor.frameId+ '-sourceedit'){
25758                 item.disable();
25759             }
25760         });
25761         this.rendered = true;
25762         
25763         // the all the btns;
25764         editor.on('editorevent', this.updateToolbar, this);
25765         // other toolbars need to implement this..
25766         //editor.on('editmodechange', this.updateToolbar, this);
25767     },
25768     
25769     
25770     
25771     /**
25772      * Protected method that will not generally be called directly. It triggers
25773      * a toolbar update by reading the markup state of the current selection in the editor.
25774      */
25775     updateToolbar: function(){
25776
25777         if(!this.editor.activated){
25778             this.editor.onFirstFocus();
25779             return;
25780         }
25781
25782         var btns = this.tb.items.map, 
25783             doc = this.editor.doc,
25784             frameId = this.editor.frameId;
25785
25786         if(!this.disable.font && !Roo.isSafari){
25787             /*
25788             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
25789             if(name != this.fontSelect.dom.value){
25790                 this.fontSelect.dom.value = name;
25791             }
25792             */
25793         }
25794         if(!this.disable.format){
25795             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
25796             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
25797             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
25798         }
25799         if(!this.disable.alignments){
25800             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
25801             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
25802             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
25803         }
25804         if(!Roo.isSafari && !this.disable.lists){
25805             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
25806             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
25807         }
25808         
25809         var ans = this.editor.getAllAncestors();
25810         if (this.formatCombo) {
25811             
25812             
25813             var store = this.formatCombo.store;
25814             this.formatCombo.setValue("");
25815             for (var i =0; i < ans.length;i++) {
25816                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
25817                     // select it..
25818                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
25819                     break;
25820                 }
25821             }
25822         }
25823         
25824         
25825         
25826         // hides menus... - so this cant be on a menu...
25827         Roo.menu.MenuMgr.hideAll();
25828
25829         //this.editorsyncValue();
25830     },
25831    
25832     
25833     createFontOptions : function(){
25834         var buf = [], fs = this.fontFamilies, ff, lc;
25835         for(var i = 0, len = fs.length; i< len; i++){
25836             ff = fs[i];
25837             lc = ff.toLowerCase();
25838             buf.push(
25839                 '<option value="',lc,'" style="font-family:',ff,';"',
25840                     (this.defaultFont == lc ? ' selected="true">' : '>'),
25841                     ff,
25842                 '</option>'
25843             );
25844         }
25845         return buf.join('');
25846     },
25847     
25848     toggleSourceEdit : function(sourceEditMode){
25849         if(sourceEditMode === undefined){
25850             sourceEditMode = !this.sourceEditMode;
25851         }
25852         this.sourceEditMode = sourceEditMode === true;
25853         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
25854         // just toggle the button?
25855         if(btn.pressed !== this.editor.sourceEditMode){
25856             btn.toggle(this.editor.sourceEditMode);
25857             return;
25858         }
25859         
25860         if(this.sourceEditMode){
25861             this.tb.items.each(function(item){
25862                 if(item.cmd != 'sourceedit'){
25863                     item.disable();
25864                 }
25865             });
25866           
25867         }else{
25868             if(this.initialized){
25869                 this.tb.items.each(function(item){
25870                     item.enable();
25871                 });
25872             }
25873             
25874         }
25875         // tell the editor that it's been pressed..
25876         this.editor.toggleSourceEdit(sourceEditMode);
25877        
25878     },
25879      /**
25880      * Object collection of toolbar tooltips for the buttons in the editor. The key
25881      * is the command id associated with that button and the value is a valid QuickTips object.
25882      * For example:
25883 <pre><code>
25884 {
25885     bold : {
25886         title: 'Bold (Ctrl+B)',
25887         text: 'Make the selected text bold.',
25888         cls: 'x-html-editor-tip'
25889     },
25890     italic : {
25891         title: 'Italic (Ctrl+I)',
25892         text: 'Make the selected text italic.',
25893         cls: 'x-html-editor-tip'
25894     },
25895     ...
25896 </code></pre>
25897     * @type Object
25898      */
25899     buttonTips : {
25900         bold : {
25901             title: 'Bold (Ctrl+B)',
25902             text: 'Make the selected text bold.',
25903             cls: 'x-html-editor-tip'
25904         },
25905         italic : {
25906             title: 'Italic (Ctrl+I)',
25907             text: 'Make the selected text italic.',
25908             cls: 'x-html-editor-tip'
25909         },
25910         underline : {
25911             title: 'Underline (Ctrl+U)',
25912             text: 'Underline the selected text.',
25913             cls: 'x-html-editor-tip'
25914         },
25915         increasefontsize : {
25916             title: 'Grow Text',
25917             text: 'Increase the font size.',
25918             cls: 'x-html-editor-tip'
25919         },
25920         decreasefontsize : {
25921             title: 'Shrink Text',
25922             text: 'Decrease the font size.',
25923             cls: 'x-html-editor-tip'
25924         },
25925         backcolor : {
25926             title: 'Text Highlight Color',
25927             text: 'Change the background color of the selected text.',
25928             cls: 'x-html-editor-tip'
25929         },
25930         forecolor : {
25931             title: 'Font Color',
25932             text: 'Change the color of the selected text.',
25933             cls: 'x-html-editor-tip'
25934         },
25935         justifyleft : {
25936             title: 'Align Text Left',
25937             text: 'Align text to the left.',
25938             cls: 'x-html-editor-tip'
25939         },
25940         justifycenter : {
25941             title: 'Center Text',
25942             text: 'Center text in the editor.',
25943             cls: 'x-html-editor-tip'
25944         },
25945         justifyright : {
25946             title: 'Align Text Right',
25947             text: 'Align text to the right.',
25948             cls: 'x-html-editor-tip'
25949         },
25950         insertunorderedlist : {
25951             title: 'Bullet List',
25952             text: 'Start a bulleted list.',
25953             cls: 'x-html-editor-tip'
25954         },
25955         insertorderedlist : {
25956             title: 'Numbered List',
25957             text: 'Start a numbered list.',
25958             cls: 'x-html-editor-tip'
25959         },
25960         createlink : {
25961             title: 'Hyperlink',
25962             text: 'Make the selected text a hyperlink.',
25963             cls: 'x-html-editor-tip'
25964         },
25965         sourceedit : {
25966             title: 'Source Edit',
25967             text: 'Switch to source editing mode.',
25968             cls: 'x-html-editor-tip'
25969         }
25970     },
25971     // private
25972     onDestroy : function(){
25973         if(this.rendered){
25974             
25975             this.tb.items.each(function(item){
25976                 if(item.menu){
25977                     item.menu.removeAll();
25978                     if(item.menu.el){
25979                         item.menu.el.destroy();
25980                     }
25981                 }
25982                 item.destroy();
25983             });
25984              
25985         }
25986     },
25987     onFirstFocus: function() {
25988         this.tb.items.each(function(item){
25989            item.enable();
25990         });
25991     }
25992 });
25993
25994
25995
25996
25997 // <script type="text/javascript">
25998 /*
25999  * Based on
26000  * Ext JS Library 1.1.1
26001  * Copyright(c) 2006-2007, Ext JS, LLC.
26002  *  
26003  
26004  */
26005
26006  
26007 /**
26008  * @class Roo.form.HtmlEditor.ToolbarContext
26009  * Context Toolbar
26010  * 
26011  * Usage:
26012  *
26013  new Roo.form.HtmlEditor({
26014     ....
26015     toolbars : [
26016         { xtype: 'ToolbarStandard', styles : {} }
26017         { xtype: 'ToolbarContext', disable : {} }
26018     ]
26019 })
26020
26021      
26022  * 
26023  * @config : {Object} disable List of elements to disable.. (not done yet.)
26024  * @config : {Object} styles  Map of styles available.
26025  * 
26026  */
26027
26028 Roo.form.HtmlEditor.ToolbarContext = function(config)
26029 {
26030     
26031     Roo.apply(this, config);
26032     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26033     // dont call parent... till later.
26034     this.styles = this.styles || {};
26035 }
26036 Roo.form.HtmlEditor.ToolbarContext.types = {
26037     'IMG' : {
26038         width : {
26039             title: "Width",
26040             width: 40
26041         },
26042         height:  {
26043             title: "Height",
26044             width: 40
26045         },
26046         align: {
26047             title: "Align",
26048             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
26049             width : 80
26050             
26051         },
26052         border: {
26053             title: "Border",
26054             width: 40
26055         },
26056         alt: {
26057             title: "Alt",
26058             width: 120
26059         },
26060         src : {
26061             title: "Src",
26062             width: 220
26063         }
26064         
26065     },
26066     'A' : {
26067         name : {
26068             title: "Name",
26069             width: 50
26070         },
26071         href:  {
26072             title: "Href",
26073             width: 220
26074         } // border?
26075         
26076     },
26077     'TABLE' : {
26078         rows : {
26079             title: "Rows",
26080             width: 20
26081         },
26082         cols : {
26083             title: "Cols",
26084             width: 20
26085         },
26086         width : {
26087             title: "Width",
26088             width: 40
26089         },
26090         height : {
26091             title: "Height",
26092             width: 40
26093         },
26094         border : {
26095             title: "Border",
26096             width: 20
26097         }
26098     },
26099     'TD' : {
26100         width : {
26101             title: "Width",
26102             width: 40
26103         },
26104         height : {
26105             title: "Height",
26106             width: 40
26107         },   
26108         align: {
26109             title: "Align",
26110             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
26111             width: 80
26112         },
26113         valign: {
26114             title: "Valign",
26115             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
26116             width: 80
26117         },
26118         colspan: {
26119             title: "Colspan",
26120             width: 20
26121             
26122         }
26123     },
26124     'INPUT' : {
26125         name : {
26126             title: "name",
26127             width: 120
26128         },
26129         value : {
26130             title: "Value",
26131             width: 120
26132         },
26133         width : {
26134             title: "Width",
26135             width: 40
26136         }
26137     },
26138     'LABEL' : {
26139         'for' : {
26140             title: "For",
26141             width: 120
26142         }
26143     },
26144     'TEXTAREA' : {
26145           name : {
26146             title: "name",
26147             width: 120
26148         },
26149         rows : {
26150             title: "Rows",
26151             width: 20
26152         },
26153         cols : {
26154             title: "Cols",
26155             width: 20
26156         }
26157     },
26158     'SELECT' : {
26159         name : {
26160             title: "name",
26161             width: 120
26162         },
26163         selectoptions : {
26164             title: "Options",
26165             width: 200
26166         }
26167     },
26168     
26169     // should we really allow this??
26170     // should this just be 
26171     'BODY' : {
26172         title : {
26173             title: "title",
26174             width: 200,
26175             disabled : true
26176         }
26177     },
26178     '*' : {
26179         // empty..
26180     }
26181 };
26182
26183
26184
26185 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
26186     
26187     tb: false,
26188     
26189     rendered: false,
26190     
26191     editor : false,
26192     /**
26193      * @cfg {Object} disable  List of toolbar elements to disable
26194          
26195      */
26196     disable : false,
26197     /**
26198      * @cfg {Object} styles List of styles 
26199      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
26200      *
26201      * These must be defined in the page, so they get rendered correctly..
26202      * .headline { }
26203      * TD.underline { }
26204      * 
26205      */
26206     styles : false,
26207     
26208     
26209     
26210     toolbars : false,
26211     
26212     init : function(editor)
26213     {
26214         this.editor = editor;
26215         
26216         
26217         var fid = editor.frameId;
26218         var etb = this;
26219         function btn(id, toggle, handler){
26220             var xid = fid + '-'+ id ;
26221             return {
26222                 id : xid,
26223                 cmd : id,
26224                 cls : 'x-btn-icon x-edit-'+id,
26225                 enableToggle:toggle !== false,
26226                 scope: editor, // was editor...
26227                 handler:handler||editor.relayBtnCmd,
26228                 clickEvent:'mousedown',
26229                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
26230                 tabIndex:-1
26231             };
26232         }
26233         // create a new element.
26234         var wdiv = editor.wrap.createChild({
26235                 tag: 'div'
26236             }, editor.wrap.dom.firstChild.nextSibling, true);
26237         
26238         // can we do this more than once??
26239         
26240          // stop form submits
26241       
26242  
26243         // disable everything...
26244         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
26245         this.toolbars = {};
26246            
26247         for (var i in  ty) {
26248           
26249             this.toolbars[i] = this.buildToolbar(ty[i],i);
26250         }
26251         this.tb = this.toolbars.BODY;
26252         this.tb.el.show();
26253         this.buildFooter();
26254         this.footer.show();
26255          
26256         this.rendered = true;
26257         
26258         // the all the btns;
26259         editor.on('editorevent', this.updateToolbar, this);
26260         // other toolbars need to implement this..
26261         //editor.on('editmodechange', this.updateToolbar, this);
26262     },
26263     
26264     
26265     
26266     /**
26267      * Protected method that will not generally be called directly. It triggers
26268      * a toolbar update by reading the markup state of the current selection in the editor.
26269      */
26270     updateToolbar: function(ignore_a,ignore_b,sel){
26271
26272         
26273         if(!this.editor.activated){
26274              this.editor.onFirstFocus();
26275             return;
26276         }
26277         var updateFooter = sel ? false : true;
26278         
26279         
26280         var ans = this.editor.getAllAncestors();
26281         
26282         // pick
26283         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
26284         
26285         if (!sel) { 
26286             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
26287             sel = sel ? sel : this.editor.doc.body;
26288             sel = sel.tagName.length ? sel : this.editor.doc.body;
26289             
26290         }
26291         // pick a menu that exists..
26292         var tn = sel.tagName.toUpperCase();
26293         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
26294         
26295         tn = sel.tagName.toUpperCase();
26296         
26297         var lastSel = this.tb.selectedNode
26298         
26299         this.tb.selectedNode = sel;
26300         
26301         // if current menu does not match..
26302         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
26303                 
26304             this.tb.el.hide();
26305             ///console.log("show: " + tn);
26306             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
26307             this.tb.el.show();
26308             // update name
26309             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
26310             
26311             
26312             // update attributes
26313             if (this.tb.fields) {
26314                 this.tb.fields.each(function(e) {
26315                    e.setValue(sel.getAttribute(e.name));
26316                 });
26317             }
26318             
26319             // update styles
26320             var st = this.tb.fields.item(0);
26321             st.store.removeAll();
26322             var cn = sel.className.split(/\s+/);
26323             
26324             var avs = [];
26325             if (this.styles['*']) {
26326                 
26327                 Roo.each(this.styles['*'], function(v) {
26328                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
26329                 });
26330             }
26331             if (this.styles[tn]) { 
26332                 Roo.each(this.styles[tn], function(v) {
26333                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
26334                 });
26335             }
26336             
26337             st.store.loadData(avs);
26338             st.collapse();
26339             st.setValue(cn);
26340             
26341             // flag our selected Node.
26342             this.tb.selectedNode = sel;
26343            
26344            
26345             Roo.menu.MenuMgr.hideAll();
26346
26347         }
26348         
26349         if (!updateFooter) {
26350             return;
26351         }
26352         // update the footer
26353         //
26354         var html = '';
26355         
26356         this.footerEls = ans.reverse();
26357         Roo.each(this.footerEls, function(a,i) {
26358             if (!a) { return; }
26359             html += html.length ? ' &gt; '  :  '';
26360             
26361             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
26362             
26363         });
26364        
26365         // 
26366         var sz = this.footDisp.up('td').getSize();
26367         this.footDisp.dom.style.width = (sz.width -10) + 'px';
26368         this.footDisp.dom.style.marginLeft = '5px';
26369         
26370         this.footDisp.dom.style.overflow = 'hidden';
26371         
26372         this.footDisp.dom.innerHTML = html;
26373             
26374         //this.editorsyncValue();
26375     },
26376    
26377        
26378     // private
26379     onDestroy : function(){
26380         if(this.rendered){
26381             
26382             this.tb.items.each(function(item){
26383                 if(item.menu){
26384                     item.menu.removeAll();
26385                     if(item.menu.el){
26386                         item.menu.el.destroy();
26387                     }
26388                 }
26389                 item.destroy();
26390             });
26391              
26392         }
26393     },
26394     onFirstFocus: function() {
26395         // need to do this for all the toolbars..
26396         this.tb.items.each(function(item){
26397            item.enable();
26398         });
26399     },
26400     buildToolbar: function(tlist, nm)
26401     {
26402         var editor = this.editor;
26403          // create a new element.
26404         var wdiv = editor.wrap.createChild({
26405                 tag: 'div'
26406             }, editor.wrap.dom.firstChild.nextSibling, true);
26407         
26408        
26409         var tb = new Roo.Toolbar(wdiv);
26410         // add the name..
26411         
26412         tb.add(nm+ ":&nbsp;");
26413         
26414         // styles...
26415         if (this.styles) {
26416             
26417             // this needs a multi-select checkbox...
26418             tb.addField( new Roo.form.ComboBox({
26419                 store: new Roo.data.SimpleStore({
26420                     id : 'val',
26421                     fields: ['val', 'selected'],
26422                     data : [] 
26423                 }),
26424                 name : 'className',
26425                 displayField:'val',
26426                 typeAhead: false,
26427                 mode: 'local',
26428                 editable : false,
26429                 triggerAction: 'all',
26430                 emptyText:'Select Style',
26431                 selectOnFocus:true,
26432                 width: 130,
26433                 listeners : {
26434                     'select': function(c, r, i) {
26435                         // initial support only for on class per el..
26436                         tb.selectedNode.className =  r ? r.get('val') : '';
26437                     }
26438                 }
26439     
26440             }));
26441         }
26442             
26443         
26444         
26445         for (var i in tlist) {
26446             
26447             var item = tlist[i];
26448             tb.add(item.title + ":&nbsp;");
26449             
26450             
26451             
26452             
26453             if (item.opts) {
26454                 // opts == pulldown..
26455                 tb.addField( new Roo.form.ComboBox({
26456                     store: new Roo.data.SimpleStore({
26457                         id : 'val',
26458                         fields: ['val'],
26459                         data : item.opts  
26460                     }),
26461                     name : i,
26462                     displayField:'val',
26463                     typeAhead: false,
26464                     mode: 'local',
26465                     editable : false,
26466                     triggerAction: 'all',
26467                     emptyText:'Select',
26468                     selectOnFocus:true,
26469                     width: item.width ? item.width  : 130,
26470                     listeners : {
26471                         'select': function(c, r, i) {
26472                             tb.selectedNode.setAttribute(c.name, r.get('val'));
26473                         }
26474                     }
26475
26476                 }));
26477                 continue;
26478                     
26479                  
26480                 
26481                 tb.addField( new Roo.form.TextField({
26482                     name: i,
26483                     width: 100,
26484                     //allowBlank:false,
26485                     value: ''
26486                 }));
26487                 continue;
26488             }
26489             tb.addField( new Roo.form.TextField({
26490                 name: i,
26491                 width: item.width,
26492                 //allowBlank:true,
26493                 value: '',
26494                 listeners: {
26495                     'change' : function(f, nv, ov) {
26496                         tb.selectedNode.setAttribute(f.name, nv);
26497                     }
26498                 }
26499             }));
26500              
26501         }
26502         tb.el.on('click', function(e){
26503             e.preventDefault(); // what does this do?
26504         });
26505         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
26506         tb.el.hide();
26507         tb.name = nm;
26508         // dont need to disable them... as they will get hidden
26509         return tb;
26510          
26511         
26512     },
26513     buildFooter : function()
26514     {
26515         
26516         var fel = this.editor.wrap.createChild();
26517         this.footer = new Roo.Toolbar(fel);
26518         // toolbar has scrolly on left / right?
26519         var footDisp= new Roo.Toolbar.Fill();
26520         var _t = this;
26521         this.footer.add(
26522             {
26523                 text : '&lt;',
26524                 xtype: 'Button',
26525                 handler : function() {
26526                     _t.footDisp.scrollTo('left',0,true)
26527                 }
26528             }
26529         );
26530         this.footer.add( footDisp );
26531         this.footer.add( 
26532             {
26533                 text : '&gt;',
26534                 xtype: 'Button',
26535                 handler : function() {
26536                     // no animation..
26537                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
26538                 }
26539             }
26540         );
26541         var fel = Roo.get(footDisp.el);
26542         fel.addClass('x-editor-context');
26543         this.footDispWrap = fel; 
26544         this.footDispWrap.overflow  = 'hidden';
26545         
26546         this.footDisp = fel.createChild();
26547         this.footDispWrap.on('click', this.onContextClick, this)
26548         
26549         
26550     },
26551     onContextClick : function (ev,dom)
26552     {
26553         ev.preventDefault();
26554         var  cn = dom.className;
26555         Roo.log(cn);
26556         if (!cn.match(/x-ed-loc-/)) {
26557             return;
26558         }
26559         var n = cn.split('-').pop();
26560         var ans = this.footerEls;
26561         var sel = ans[n];
26562         
26563          // pick
26564         var range = this.editor.createRange();
26565         
26566         range.selectNodeContents(sel);
26567         //range.selectNode(sel);
26568         
26569         
26570         var selection = this.editor.getSelection();
26571         selection.removeAllRanges();
26572         selection.addRange(range);
26573         
26574         
26575         
26576         this.updateToolbar(null, null, sel);
26577         
26578         
26579     }
26580     
26581     
26582     
26583     
26584     
26585 });
26586
26587
26588
26589
26590
26591 /*
26592  * Based on:
26593  * Ext JS Library 1.1.1
26594  * Copyright(c) 2006-2007, Ext JS, LLC.
26595  *
26596  * Originally Released Under LGPL - original licence link has changed is not relivant.
26597  *
26598  * Fork - LGPL
26599  * <script type="text/javascript">
26600  */
26601  
26602 /**
26603  * @class Roo.form.BasicForm
26604  * @extends Roo.util.Observable
26605  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
26606  * @constructor
26607  * @param {String/HTMLElement/Roo.Element} el The form element or its id
26608  * @param {Object} config Configuration options
26609  */
26610 Roo.form.BasicForm = function(el, config){
26611     this.allItems = [];
26612     this.childForms = [];
26613     Roo.apply(this, config);
26614     /*
26615      * The Roo.form.Field items in this form.
26616      * @type MixedCollection
26617      */
26618      
26619      
26620     this.items = new Roo.util.MixedCollection(false, function(o){
26621         return o.id || (o.id = Roo.id());
26622     });
26623     this.addEvents({
26624         /**
26625          * @event beforeaction
26626          * Fires before any action is performed. Return false to cancel the action.
26627          * @param {Form} this
26628          * @param {Action} action The action to be performed
26629          */
26630         beforeaction: true,
26631         /**
26632          * @event actionfailed
26633          * Fires when an action fails.
26634          * @param {Form} this
26635          * @param {Action} action The action that failed
26636          */
26637         actionfailed : true,
26638         /**
26639          * @event actioncomplete
26640          * Fires when an action is completed.
26641          * @param {Form} this
26642          * @param {Action} action The action that completed
26643          */
26644         actioncomplete : true
26645     });
26646     if(el){
26647         this.initEl(el);
26648     }
26649     Roo.form.BasicForm.superclass.constructor.call(this);
26650 };
26651
26652 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
26653     /**
26654      * @cfg {String} method
26655      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
26656      */
26657     /**
26658      * @cfg {DataReader} reader
26659      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
26660      * This is optional as there is built-in support for processing JSON.
26661      */
26662     /**
26663      * @cfg {DataReader} errorReader
26664      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
26665      * This is completely optional as there is built-in support for processing JSON.
26666      */
26667     /**
26668      * @cfg {String} url
26669      * The URL to use for form actions if one isn't supplied in the action options.
26670      */
26671     /**
26672      * @cfg {Boolean} fileUpload
26673      * Set to true if this form is a file upload.
26674      */
26675      
26676     /**
26677      * @cfg {Object} baseParams
26678      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
26679      */
26680      /**
26681      
26682     /**
26683      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
26684      */
26685     timeout: 30,
26686
26687     // private
26688     activeAction : null,
26689
26690     /**
26691      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
26692      * or setValues() data instead of when the form was first created.
26693      */
26694     trackResetOnLoad : false,
26695     
26696     
26697     /**
26698      * childForms - used for multi-tab forms
26699      * @type {Array}
26700      */
26701     childForms : false,
26702     
26703     /**
26704      * allItems - full list of fields.
26705      * @type {Array}
26706      */
26707     allItems : false,
26708     
26709     /**
26710      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
26711      * element by passing it or its id or mask the form itself by passing in true.
26712      * @type Mixed
26713      */
26714     waitMsgTarget : false,
26715
26716     // private
26717     initEl : function(el){
26718         this.el = Roo.get(el);
26719         this.id = this.el.id || Roo.id();
26720         this.el.on('submit', this.onSubmit, this);
26721         this.el.addClass('x-form');
26722     },
26723
26724     // private
26725     onSubmit : function(e){
26726         e.stopEvent();
26727     },
26728
26729     /**
26730      * Returns true if client-side validation on the form is successful.
26731      * @return Boolean
26732      */
26733     isValid : function(){
26734         var valid = true;
26735         this.items.each(function(f){
26736            if(!f.validate()){
26737                valid = false;
26738            }
26739         });
26740         return valid;
26741     },
26742
26743     /**
26744      * Returns true if any fields in this form have changed since their original load.
26745      * @return Boolean
26746      */
26747     isDirty : function(){
26748         var dirty = false;
26749         this.items.each(function(f){
26750            if(f.isDirty()){
26751                dirty = true;
26752                return false;
26753            }
26754         });
26755         return dirty;
26756     },
26757
26758     /**
26759      * Performs a predefined action (submit or load) or custom actions you define on this form.
26760      * @param {String} actionName The name of the action type
26761      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
26762      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
26763      * accept other config options):
26764      * <pre>
26765 Property          Type             Description
26766 ----------------  ---------------  ----------------------------------------------------------------------------------
26767 url               String           The url for the action (defaults to the form's url)
26768 method            String           The form method to use (defaults to the form's method, or POST if not defined)
26769 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
26770 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
26771                                    validate the form on the client (defaults to false)
26772      * </pre>
26773      * @return {BasicForm} this
26774      */
26775     doAction : function(action, options){
26776         if(typeof action == 'string'){
26777             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
26778         }
26779         if(this.fireEvent('beforeaction', this, action) !== false){
26780             this.beforeAction(action);
26781             action.run.defer(100, action);
26782         }
26783         return this;
26784     },
26785
26786     /**
26787      * Shortcut to do a submit action.
26788      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26789      * @return {BasicForm} this
26790      */
26791     submit : function(options){
26792         this.doAction('submit', options);
26793         return this;
26794     },
26795
26796     /**
26797      * Shortcut to do a load action.
26798      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26799      * @return {BasicForm} this
26800      */
26801     load : function(options){
26802         this.doAction('load', options);
26803         return this;
26804     },
26805
26806     /**
26807      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
26808      * @param {Record} record The record to edit
26809      * @return {BasicForm} this
26810      */
26811     updateRecord : function(record){
26812         record.beginEdit();
26813         var fs = record.fields;
26814         fs.each(function(f){
26815             var field = this.findField(f.name);
26816             if(field){
26817                 record.set(f.name, field.getValue());
26818             }
26819         }, this);
26820         record.endEdit();
26821         return this;
26822     },
26823
26824     /**
26825      * Loads an Roo.data.Record into this form.
26826      * @param {Record} record The record to load
26827      * @return {BasicForm} this
26828      */
26829     loadRecord : function(record){
26830         this.setValues(record.data);
26831         return this;
26832     },
26833
26834     // private
26835     beforeAction : function(action){
26836         var o = action.options;
26837         
26838        
26839         if(this.waitMsgTarget === true){
26840             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
26841         }else if(this.waitMsgTarget){
26842             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
26843             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
26844         }else {
26845             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
26846         }
26847          
26848     },
26849
26850     // private
26851     afterAction : function(action, success){
26852         this.activeAction = null;
26853         var o = action.options;
26854         
26855         if(this.waitMsgTarget === true){
26856             this.el.unmask();
26857         }else if(this.waitMsgTarget){
26858             this.waitMsgTarget.unmask();
26859         }else{
26860             Roo.MessageBox.updateProgress(1);
26861             Roo.MessageBox.hide();
26862         }
26863          
26864         if(success){
26865             if(o.reset){
26866                 this.reset();
26867             }
26868             Roo.callback(o.success, o.scope, [this, action]);
26869             this.fireEvent('actioncomplete', this, action);
26870             
26871         }else{
26872             Roo.callback(o.failure, o.scope, [this, action]);
26873             // show an error message if no failed handler is set..
26874             if (!this.hasListener('actionfailed')) {
26875                 Roo.MessageBox.alert("Error",
26876                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
26877                         action.result.errorMsg :
26878                         "Saving Failed, please check your entries"
26879                 );
26880             }
26881             
26882             this.fireEvent('actionfailed', this, action);
26883         }
26884         
26885     },
26886
26887     /**
26888      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
26889      * @param {String} id The value to search for
26890      * @return Field
26891      */
26892     findField : function(id){
26893         var field = this.items.get(id);
26894         if(!field){
26895             this.items.each(function(f){
26896                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
26897                     field = f;
26898                     return false;
26899                 }
26900             });
26901         }
26902         return field || null;
26903     },
26904
26905     /**
26906      * Add a secondary form to this one, 
26907      * Used to provide tabbed forms. One form is primary, with hidden values 
26908      * which mirror the elements from the other forms.
26909      * 
26910      * @param {Roo.form.Form} form to add.
26911      * 
26912      */
26913     addForm : function(form)
26914     {
26915        
26916         if (this.childForms.indexOf(form) > -1) {
26917             // already added..
26918             return;
26919         }
26920         this.childForms.push(form);
26921         var n = '';
26922         Roo.each(form.allItems, function (fe) {
26923             
26924             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
26925             if (this.findField(n)) { // already added..
26926                 return;
26927             }
26928             var add = new Roo.form.Hidden({
26929                 name : n
26930             });
26931             add.render(this.el);
26932             
26933             this.add( add );
26934         }, this);
26935         
26936     },
26937     /**
26938      * Mark fields in this form invalid in bulk.
26939      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
26940      * @return {BasicForm} this
26941      */
26942     markInvalid : function(errors){
26943         if(errors instanceof Array){
26944             for(var i = 0, len = errors.length; i < len; i++){
26945                 var fieldError = errors[i];
26946                 var f = this.findField(fieldError.id);
26947                 if(f){
26948                     f.markInvalid(fieldError.msg);
26949                 }
26950             }
26951         }else{
26952             var field, id;
26953             for(id in errors){
26954                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
26955                     field.markInvalid(errors[id]);
26956                 }
26957             }
26958         }
26959         Roo.each(this.childForms || [], function (f) {
26960             f.markInvalid(errors);
26961         });
26962         
26963         return this;
26964     },
26965
26966     /**
26967      * Set values for fields in this form in bulk.
26968      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
26969      * @return {BasicForm} this
26970      */
26971     setValues : function(values){
26972         if(values instanceof Array){ // array of objects
26973             for(var i = 0, len = values.length; i < len; i++){
26974                 var v = values[i];
26975                 var f = this.findField(v.id);
26976                 if(f){
26977                     f.setValue(v.value);
26978                     if(this.trackResetOnLoad){
26979                         f.originalValue = f.getValue();
26980                     }
26981                 }
26982             }
26983         }else{ // object hash
26984             var field, id;
26985             for(id in values){
26986                 if(typeof values[id] != 'function' && (field = this.findField(id))){
26987                     
26988                     if (field.setFromData && 
26989                         field.valueField && 
26990                         field.displayField &&
26991                         // combos' with local stores can 
26992                         // be queried via setValue()
26993                         // to set their value..
26994                         (field.store && !field.store.isLocal)
26995                         ) {
26996                         // it's a combo
26997                         var sd = { };
26998                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
26999                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
27000                         field.setFromData(sd);
27001                         
27002                     } else {
27003                         field.setValue(values[id]);
27004                     }
27005                     
27006                     
27007                     if(this.trackResetOnLoad){
27008                         field.originalValue = field.getValue();
27009                     }
27010                 }
27011             }
27012         }
27013          
27014         Roo.each(this.childForms || [], function (f) {
27015             f.setValues(values);
27016         });
27017                 
27018         return this;
27019     },
27020
27021     /**
27022      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
27023      * they are returned as an array.
27024      * @param {Boolean} asString
27025      * @return {Object}
27026      */
27027     getValues : function(asString){
27028         if (this.childForms) {
27029             // copy values from the child forms
27030             Roo.each(this.childForms, function (f) {
27031                 this.setValues(f.getValues());
27032             }, this);
27033         }
27034         
27035         
27036         
27037         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
27038         if(asString === true){
27039             return fs;
27040         }
27041         return Roo.urlDecode(fs);
27042     },
27043     
27044     /**
27045      * Returns the fields in this form as an object with key/value pairs. 
27046      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
27047      * @return {Object}
27048      */
27049     getFieldValues : function(with_hidden)
27050     {
27051         if (this.childForms) {
27052             // copy values from the child forms
27053             // should this call getFieldValues - probably not as we do not currently copy
27054             // hidden fields when we generate..
27055             Roo.each(this.childForms, function (f) {
27056                 this.setValues(f.getValues());
27057             }, this);
27058         }
27059         
27060         var ret = {};
27061         this.items.each(function(f){
27062             if (!f.getName()) {
27063                 return;
27064             }
27065             var v = f.getValue();
27066             // not sure if this supported any more..
27067             if ((typeof(v) == 'object') && f.getRawValue) {
27068                 v = f.getRawValue() ; // dates..
27069             }
27070             // combo boxes where name != hiddenName...
27071             if (f.name != f.getName()) {
27072                 ret[f.name] = f.getRawValue();
27073             }
27074             ret[f.getName()] = v;
27075         });
27076         
27077         return ret;
27078     },
27079
27080     /**
27081      * Clears all invalid messages in this form.
27082      * @return {BasicForm} this
27083      */
27084     clearInvalid : function(){
27085         this.items.each(function(f){
27086            f.clearInvalid();
27087         });
27088         
27089         Roo.each(this.childForms || [], function (f) {
27090             f.clearInvalid();
27091         });
27092         
27093         
27094         return this;
27095     },
27096
27097     /**
27098      * Resets this form.
27099      * @return {BasicForm} this
27100      */
27101     reset : function(){
27102         this.items.each(function(f){
27103             f.reset();
27104         });
27105         
27106         Roo.each(this.childForms || [], function (f) {
27107             f.reset();
27108         });
27109        
27110         
27111         return this;
27112     },
27113
27114     /**
27115      * Add Roo.form components to this form.
27116      * @param {Field} field1
27117      * @param {Field} field2 (optional)
27118      * @param {Field} etc (optional)
27119      * @return {BasicForm} this
27120      */
27121     add : function(){
27122         this.items.addAll(Array.prototype.slice.call(arguments, 0));
27123         return this;
27124     },
27125
27126
27127     /**
27128      * Removes a field from the items collection (does NOT remove its markup).
27129      * @param {Field} field
27130      * @return {BasicForm} this
27131      */
27132     remove : function(field){
27133         this.items.remove(field);
27134         return this;
27135     },
27136
27137     /**
27138      * Looks at the fields in this form, checks them for an id attribute,
27139      * and calls applyTo on the existing dom element with that id.
27140      * @return {BasicForm} this
27141      */
27142     render : function(){
27143         this.items.each(function(f){
27144             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
27145                 f.applyTo(f.id);
27146             }
27147         });
27148         return this;
27149     },
27150
27151     /**
27152      * Calls {@link Ext#apply} for all fields in this form with the passed object.
27153      * @param {Object} values
27154      * @return {BasicForm} this
27155      */
27156     applyToFields : function(o){
27157         this.items.each(function(f){
27158            Roo.apply(f, o);
27159         });
27160         return this;
27161     },
27162
27163     /**
27164      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
27165      * @param {Object} values
27166      * @return {BasicForm} this
27167      */
27168     applyIfToFields : function(o){
27169         this.items.each(function(f){
27170            Roo.applyIf(f, o);
27171         });
27172         return this;
27173     }
27174 });
27175
27176 // back compat
27177 Roo.BasicForm = Roo.form.BasicForm;/*
27178  * Based on:
27179  * Ext JS Library 1.1.1
27180  * Copyright(c) 2006-2007, Ext JS, LLC.
27181  *
27182  * Originally Released Under LGPL - original licence link has changed is not relivant.
27183  *
27184  * Fork - LGPL
27185  * <script type="text/javascript">
27186  */
27187
27188 /**
27189  * @class Roo.form.Form
27190  * @extends Roo.form.BasicForm
27191  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
27192  * @constructor
27193  * @param {Object} config Configuration options
27194  */
27195 Roo.form.Form = function(config){
27196     var xitems =  [];
27197     if (config.items) {
27198         xitems = config.items;
27199         delete config.items;
27200     }
27201    
27202     
27203     Roo.form.Form.superclass.constructor.call(this, null, config);
27204     this.url = this.url || this.action;
27205     if(!this.root){
27206         this.root = new Roo.form.Layout(Roo.applyIf({
27207             id: Roo.id()
27208         }, config));
27209     }
27210     this.active = this.root;
27211     /**
27212      * Array of all the buttons that have been added to this form via {@link addButton}
27213      * @type Array
27214      */
27215     this.buttons = [];
27216     this.allItems = [];
27217     this.addEvents({
27218         /**
27219          * @event clientvalidation
27220          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
27221          * @param {Form} this
27222          * @param {Boolean} valid true if the form has passed client-side validation
27223          */
27224         clientvalidation: true,
27225         /**
27226          * @event rendered
27227          * Fires when the form is rendered
27228          * @param {Roo.form.Form} form
27229          */
27230         rendered : true
27231     });
27232     
27233     if (this.progressUrl) {
27234             // push a hidden field onto the list of fields..
27235             this.addxtype( {
27236                     xns: Roo.form, 
27237                     xtype : 'Hidden', 
27238                     name : 'UPLOAD_IDENTIFIER' 
27239             });
27240         }
27241         
27242     
27243     Roo.each(xitems, this.addxtype, this);
27244     
27245     
27246     
27247 };
27248
27249 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
27250     /**
27251      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
27252      */
27253     /**
27254      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
27255      */
27256     /**
27257      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
27258      */
27259     buttonAlign:'center',
27260
27261     /**
27262      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
27263      */
27264     minButtonWidth:75,
27265
27266     /**
27267      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
27268      * This property cascades to child containers if not set.
27269      */
27270     labelAlign:'left',
27271
27272     /**
27273      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
27274      * fires a looping event with that state. This is required to bind buttons to the valid
27275      * state using the config value formBind:true on the button.
27276      */
27277     monitorValid : false,
27278
27279     /**
27280      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
27281      */
27282     monitorPoll : 200,
27283     
27284     /**
27285      * @cfg {String} progressUrl - Url to return progress data 
27286      */
27287     
27288     progressUrl : false,
27289   
27290     /**
27291      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
27292      * fields are added and the column is closed. If no fields are passed the column remains open
27293      * until end() is called.
27294      * @param {Object} config The config to pass to the column
27295      * @param {Field} field1 (optional)
27296      * @param {Field} field2 (optional)
27297      * @param {Field} etc (optional)
27298      * @return Column The column container object
27299      */
27300     column : function(c){
27301         var col = new Roo.form.Column(c);
27302         this.start(col);
27303         if(arguments.length > 1){ // duplicate code required because of Opera
27304             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
27305             this.end();
27306         }
27307         return col;
27308     },
27309
27310     /**
27311      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
27312      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
27313      * until end() is called.
27314      * @param {Object} config The config to pass to the fieldset
27315      * @param {Field} field1 (optional)
27316      * @param {Field} field2 (optional)
27317      * @param {Field} etc (optional)
27318      * @return FieldSet The fieldset container object
27319      */
27320     fieldset : function(c){
27321         var fs = new Roo.form.FieldSet(c);
27322         this.start(fs);
27323         if(arguments.length > 1){ // duplicate code required because of Opera
27324             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
27325             this.end();
27326         }
27327         return fs;
27328     },
27329
27330     /**
27331      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
27332      * fields are added and the container is closed. If no fields are passed the container remains open
27333      * until end() is called.
27334      * @param {Object} config The config to pass to the Layout
27335      * @param {Field} field1 (optional)
27336      * @param {Field} field2 (optional)
27337      * @param {Field} etc (optional)
27338      * @return Layout The container object
27339      */
27340     container : function(c){
27341         var l = new Roo.form.Layout(c);
27342         this.start(l);
27343         if(arguments.length > 1){ // duplicate code required because of Opera
27344             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
27345             this.end();
27346         }
27347         return l;
27348     },
27349
27350     /**
27351      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
27352      * @param {Object} container A Roo.form.Layout or subclass of Layout
27353      * @return {Form} this
27354      */
27355     start : function(c){
27356         // cascade label info
27357         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
27358         this.active.stack.push(c);
27359         c.ownerCt = this.active;
27360         this.active = c;
27361         return this;
27362     },
27363
27364     /**
27365      * Closes the current open container
27366      * @return {Form} this
27367      */
27368     end : function(){
27369         if(this.active == this.root){
27370             return this;
27371         }
27372         this.active = this.active.ownerCt;
27373         return this;
27374     },
27375
27376     /**
27377      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
27378      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
27379      * as the label of the field.
27380      * @param {Field} field1
27381      * @param {Field} field2 (optional)
27382      * @param {Field} etc. (optional)
27383      * @return {Form} this
27384      */
27385     add : function(){
27386         this.active.stack.push.apply(this.active.stack, arguments);
27387         this.allItems.push.apply(this.allItems,arguments);
27388         var r = [];
27389         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
27390             if(a[i].isFormField){
27391                 r.push(a[i]);
27392             }
27393         }
27394         if(r.length > 0){
27395             Roo.form.Form.superclass.add.apply(this, r);
27396         }
27397         return this;
27398     },
27399     
27400
27401     
27402     
27403     
27404      /**
27405      * Find any element that has been added to a form, using it's ID or name
27406      * This can include framesets, columns etc. along with regular fields..
27407      * @param {String} id - id or name to find.
27408      
27409      * @return {Element} e - or false if nothing found.
27410      */
27411     findbyId : function(id)
27412     {
27413         var ret = false;
27414         if (!id) {
27415             return ret;
27416         }
27417         Roo.each(this.allItems, function(f){
27418             if (f.id == id || f.name == id ){
27419                 ret = f;
27420                 return false;
27421             }
27422         });
27423         return ret;
27424     },
27425
27426     
27427     
27428     /**
27429      * Render this form into the passed container. This should only be called once!
27430      * @param {String/HTMLElement/Element} container The element this component should be rendered into
27431      * @return {Form} this
27432      */
27433     render : function(ct)
27434     {
27435         
27436         
27437         
27438         ct = Roo.get(ct);
27439         var o = this.autoCreate || {
27440             tag: 'form',
27441             method : this.method || 'POST',
27442             id : this.id || Roo.id()
27443         };
27444         this.initEl(ct.createChild(o));
27445
27446         this.root.render(this.el);
27447         
27448        
27449              
27450         this.items.each(function(f){
27451             f.render('x-form-el-'+f.id);
27452         });
27453
27454         if(this.buttons.length > 0){
27455             // tables are required to maintain order and for correct IE layout
27456             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
27457                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
27458                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
27459             }}, null, true);
27460             var tr = tb.getElementsByTagName('tr')[0];
27461             for(var i = 0, len = this.buttons.length; i < len; i++) {
27462                 var b = this.buttons[i];
27463                 var td = document.createElement('td');
27464                 td.className = 'x-form-btn-td';
27465                 b.render(tr.appendChild(td));
27466             }
27467         }
27468         if(this.monitorValid){ // initialize after render
27469             this.startMonitoring();
27470         }
27471         this.fireEvent('rendered', this);
27472         return this;
27473     },
27474
27475     /**
27476      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
27477      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
27478      * object or a valid Roo.DomHelper element config
27479      * @param {Function} handler The function called when the button is clicked
27480      * @param {Object} scope (optional) The scope of the handler function
27481      * @return {Roo.Button}
27482      */
27483     addButton : function(config, handler, scope){
27484         var bc = {
27485             handler: handler,
27486             scope: scope,
27487             minWidth: this.minButtonWidth,
27488             hideParent:true
27489         };
27490         if(typeof config == "string"){
27491             bc.text = config;
27492         }else{
27493             Roo.apply(bc, config);
27494         }
27495         var btn = new Roo.Button(null, bc);
27496         this.buttons.push(btn);
27497         return btn;
27498     },
27499
27500      /**
27501      * Adds a series of form elements (using the xtype property as the factory method.
27502      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
27503      * @param {Object} config 
27504      */
27505     
27506     addxtype : function()
27507     {
27508         var ar = Array.prototype.slice.call(arguments, 0);
27509         var ret = false;
27510         for(var i = 0; i < ar.length; i++) {
27511             if (!ar[i]) {
27512                 continue; // skip -- if this happends something invalid got sent, we 
27513                 // should ignore it, as basically that interface element will not show up
27514                 // and that should be pretty obvious!!
27515             }
27516             
27517             if (Roo.form[ar[i].xtype]) {
27518                 ar[i].form = this;
27519                 var fe = Roo.factory(ar[i], Roo.form);
27520                 if (!ret) {
27521                     ret = fe;
27522                 }
27523                 fe.form = this;
27524                 if (fe.store) {
27525                     fe.store.form = this;
27526                 }
27527                 if (fe.isLayout) {  
27528                          
27529                     this.start(fe);
27530                     this.allItems.push(fe);
27531                     if (fe.items && fe.addxtype) {
27532                         fe.addxtype.apply(fe, fe.items);
27533                         delete fe.items;
27534                     }
27535                      this.end();
27536                     continue;
27537                 }
27538                 
27539                 
27540                  
27541                 this.add(fe);
27542               //  console.log('adding ' + ar[i].xtype);
27543             }
27544             if (ar[i].xtype == 'Button') {  
27545                 //console.log('adding button');
27546                 //console.log(ar[i]);
27547                 this.addButton(ar[i]);
27548                 this.allItems.push(fe);
27549                 continue;
27550             }
27551             
27552             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
27553                 alert('end is not supported on xtype any more, use items');
27554             //    this.end();
27555             //    //console.log('adding end');
27556             }
27557             
27558         }
27559         return ret;
27560     },
27561     
27562     /**
27563      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
27564      * option "monitorValid"
27565      */
27566     startMonitoring : function(){
27567         if(!this.bound){
27568             this.bound = true;
27569             Roo.TaskMgr.start({
27570                 run : this.bindHandler,
27571                 interval : this.monitorPoll || 200,
27572                 scope: this
27573             });
27574         }
27575     },
27576
27577     /**
27578      * Stops monitoring of the valid state of this form
27579      */
27580     stopMonitoring : function(){
27581         this.bound = false;
27582     },
27583
27584     // private
27585     bindHandler : function(){
27586         if(!this.bound){
27587             return false; // stops binding
27588         }
27589         var valid = true;
27590         this.items.each(function(f){
27591             if(!f.isValid(true)){
27592                 valid = false;
27593                 return false;
27594             }
27595         });
27596         for(var i = 0, len = this.buttons.length; i < len; i++){
27597             var btn = this.buttons[i];
27598             if(btn.formBind === true && btn.disabled === valid){
27599                 btn.setDisabled(!valid);
27600             }
27601         }
27602         this.fireEvent('clientvalidation', this, valid);
27603     }
27604     
27605     
27606     
27607     
27608     
27609     
27610     
27611     
27612 });
27613
27614
27615 // back compat
27616 Roo.Form = Roo.form.Form;
27617 /*
27618  * Based on:
27619  * Ext JS Library 1.1.1
27620  * Copyright(c) 2006-2007, Ext JS, LLC.
27621  *
27622  * Originally Released Under LGPL - original licence link has changed is not relivant.
27623  *
27624  * Fork - LGPL
27625  * <script type="text/javascript">
27626  */
27627  
27628  /**
27629  * @class Roo.form.Action
27630  * Internal Class used to handle form actions
27631  * @constructor
27632  * @param {Roo.form.BasicForm} el The form element or its id
27633  * @param {Object} config Configuration options
27634  */
27635  
27636  
27637 // define the action interface
27638 Roo.form.Action = function(form, options){
27639     this.form = form;
27640     this.options = options || {};
27641 };
27642 /**
27643  * Client Validation Failed
27644  * @const 
27645  */
27646 Roo.form.Action.CLIENT_INVALID = 'client';
27647 /**
27648  * Server Validation Failed
27649  * @const 
27650  */
27651  Roo.form.Action.SERVER_INVALID = 'server';
27652  /**
27653  * Connect to Server Failed
27654  * @const 
27655  */
27656 Roo.form.Action.CONNECT_FAILURE = 'connect';
27657 /**
27658  * Reading Data from Server Failed
27659  * @const 
27660  */
27661 Roo.form.Action.LOAD_FAILURE = 'load';
27662
27663 Roo.form.Action.prototype = {
27664     type : 'default',
27665     failureType : undefined,
27666     response : undefined,
27667     result : undefined,
27668
27669     // interface method
27670     run : function(options){
27671
27672     },
27673
27674     // interface method
27675     success : function(response){
27676
27677     },
27678
27679     // interface method
27680     handleResponse : function(response){
27681
27682     },
27683
27684     // default connection failure
27685     failure : function(response){
27686         
27687         this.response = response;
27688         this.failureType = Roo.form.Action.CONNECT_FAILURE;
27689         this.form.afterAction(this, false);
27690     },
27691
27692     processResponse : function(response){
27693         this.response = response;
27694         if(!response.responseText){
27695             return true;
27696         }
27697         this.result = this.handleResponse(response);
27698         return this.result;
27699     },
27700
27701     // utility functions used internally
27702     getUrl : function(appendParams){
27703         var url = this.options.url || this.form.url || this.form.el.dom.action;
27704         if(appendParams){
27705             var p = this.getParams();
27706             if(p){
27707                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
27708             }
27709         }
27710         return url;
27711     },
27712
27713     getMethod : function(){
27714         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
27715     },
27716
27717     getParams : function(){
27718         var bp = this.form.baseParams;
27719         var p = this.options.params;
27720         if(p){
27721             if(typeof p == "object"){
27722                 p = Roo.urlEncode(Roo.applyIf(p, bp));
27723             }else if(typeof p == 'string' && bp){
27724                 p += '&' + Roo.urlEncode(bp);
27725             }
27726         }else if(bp){
27727             p = Roo.urlEncode(bp);
27728         }
27729         return p;
27730     },
27731
27732     createCallback : function(){
27733         return {
27734             success: this.success,
27735             failure: this.failure,
27736             scope: this,
27737             timeout: (this.form.timeout*1000),
27738             upload: this.form.fileUpload ? this.success : undefined
27739         };
27740     }
27741 };
27742
27743 Roo.form.Action.Submit = function(form, options){
27744     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
27745 };
27746
27747 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
27748     type : 'submit',
27749
27750     haveProgress : false,
27751     uploadComplete : false,
27752     
27753     // uploadProgress indicator.
27754     uploadProgress : function()
27755     {
27756         if (!this.form.progressUrl) {
27757             return;
27758         }
27759         
27760         if (!this.haveProgress) {
27761             Roo.MessageBox.progress("Uploading", "Uploading");
27762         }
27763         if (this.uploadComplete) {
27764            Roo.MessageBox.hide();
27765            return;
27766         }
27767         
27768         this.haveProgress = true;
27769    
27770         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
27771         
27772         var c = new Roo.data.Connection();
27773         c.request({
27774             url : this.form.progressUrl,
27775             params: {
27776                 id : uid
27777             },
27778             method: 'GET',
27779             success : function(req){
27780                //console.log(data);
27781                 var rdata = false;
27782                 var edata;
27783                 try  {
27784                    rdata = Roo.decode(req.responseText)
27785                 } catch (e) {
27786                     Roo.log("Invalid data from server..");
27787                     Roo.log(edata);
27788                     return;
27789                 }
27790                 if (!rdata || !rdata.success) {
27791                     Roo.log(rdata);
27792                     return;
27793                 }
27794                 var data = rdata.data;
27795                 
27796                 if (this.uploadComplete) {
27797                    Roo.MessageBox.hide();
27798                    return;
27799                 }
27800                    
27801                 if (data){
27802                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
27803                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
27804                     );
27805                 }
27806                 this.uploadProgress.defer(2000,this);
27807             },
27808        
27809             failure: function(data) {
27810                 Roo.log('progress url failed ');
27811                 Roo.log(data);
27812             },
27813             scope : this
27814         });
27815            
27816     },
27817     
27818     
27819     run : function()
27820     {
27821         // run get Values on the form, so it syncs any secondary forms.
27822         this.form.getValues();
27823         
27824         var o = this.options;
27825         var method = this.getMethod();
27826         var isPost = method == 'POST';
27827         if(o.clientValidation === false || this.form.isValid()){
27828             
27829             if (this.form.progressUrl) {
27830                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
27831                     (new Date() * 1) + '' + Math.random());
27832                     
27833             } 
27834             
27835             
27836             Roo.Ajax.request(Roo.apply(this.createCallback(), {
27837                 form:this.form.el.dom,
27838                 url:this.getUrl(!isPost),
27839                 method: method,
27840                 params:isPost ? this.getParams() : null,
27841                 isUpload: this.form.fileUpload
27842             }));
27843             
27844             this.uploadProgress();
27845
27846         }else if (o.clientValidation !== false){ // client validation failed
27847             this.failureType = Roo.form.Action.CLIENT_INVALID;
27848             this.form.afterAction(this, false);
27849         }
27850     },
27851
27852     success : function(response)
27853     {
27854         this.uploadComplete= true;
27855         if (this.haveProgress) {
27856             Roo.MessageBox.hide();
27857         }
27858         
27859         
27860         var result = this.processResponse(response);
27861         if(result === true || result.success){
27862             this.form.afterAction(this, true);
27863             return;
27864         }
27865         if(result.errors){
27866             this.form.markInvalid(result.errors);
27867             this.failureType = Roo.form.Action.SERVER_INVALID;
27868         }
27869         this.form.afterAction(this, false);
27870     },
27871     failure : function(response)
27872     {
27873         this.uploadComplete= true;
27874         if (this.haveProgress) {
27875             Roo.MessageBox.hide();
27876         }
27877         
27878         this.response = response;
27879         this.failureType = Roo.form.Action.CONNECT_FAILURE;
27880         this.form.afterAction(this, false);
27881     },
27882     
27883     handleResponse : function(response){
27884         if(this.form.errorReader){
27885             var rs = this.form.errorReader.read(response);
27886             var errors = [];
27887             if(rs.records){
27888                 for(var i = 0, len = rs.records.length; i < len; i++) {
27889                     var r = rs.records[i];
27890                     errors[i] = r.data;
27891                 }
27892             }
27893             if(errors.length < 1){
27894                 errors = null;
27895             }
27896             return {
27897                 success : rs.success,
27898                 errors : errors
27899             };
27900         }
27901         var ret = false;
27902         try {
27903             ret = Roo.decode(response.responseText);
27904         } catch (e) {
27905             ret = {
27906                 success: false,
27907                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
27908                 errors : []
27909             };
27910         }
27911         return ret;
27912         
27913     }
27914 });
27915
27916
27917 Roo.form.Action.Load = function(form, options){
27918     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
27919     this.reader = this.form.reader;
27920 };
27921
27922 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
27923     type : 'load',
27924
27925     run : function(){
27926         
27927         Roo.Ajax.request(Roo.apply(
27928                 this.createCallback(), {
27929                     method:this.getMethod(),
27930                     url:this.getUrl(false),
27931                     params:this.getParams()
27932         }));
27933     },
27934
27935     success : function(response){
27936         
27937         var result = this.processResponse(response);
27938         if(result === true || !result.success || !result.data){
27939             this.failureType = Roo.form.Action.LOAD_FAILURE;
27940             this.form.afterAction(this, false);
27941             return;
27942         }
27943         this.form.clearInvalid();
27944         this.form.setValues(result.data);
27945         this.form.afterAction(this, true);
27946     },
27947
27948     handleResponse : function(response){
27949         if(this.form.reader){
27950             var rs = this.form.reader.read(response);
27951             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
27952             return {
27953                 success : rs.success,
27954                 data : data
27955             };
27956         }
27957         return Roo.decode(response.responseText);
27958     }
27959 });
27960
27961 Roo.form.Action.ACTION_TYPES = {
27962     'load' : Roo.form.Action.Load,
27963     'submit' : Roo.form.Action.Submit
27964 };/*
27965  * Based on:
27966  * Ext JS Library 1.1.1
27967  * Copyright(c) 2006-2007, Ext JS, LLC.
27968  *
27969  * Originally Released Under LGPL - original licence link has changed is not relivant.
27970  *
27971  * Fork - LGPL
27972  * <script type="text/javascript">
27973  */
27974  
27975 /**
27976  * @class Roo.form.Layout
27977  * @extends Roo.Component
27978  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
27979  * @constructor
27980  * @param {Object} config Configuration options
27981  */
27982 Roo.form.Layout = function(config){
27983     var xitems = [];
27984     if (config.items) {
27985         xitems = config.items;
27986         delete config.items;
27987     }
27988     Roo.form.Layout.superclass.constructor.call(this, config);
27989     this.stack = [];
27990     Roo.each(xitems, this.addxtype, this);
27991      
27992 };
27993
27994 Roo.extend(Roo.form.Layout, Roo.Component, {
27995     /**
27996      * @cfg {String/Object} autoCreate
27997      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
27998      */
27999     /**
28000      * @cfg {String/Object/Function} style
28001      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
28002      * a function which returns such a specification.
28003      */
28004     /**
28005      * @cfg {String} labelAlign
28006      * Valid values are "left," "top" and "right" (defaults to "left")
28007      */
28008     /**
28009      * @cfg {Number} labelWidth
28010      * Fixed width in pixels of all field labels (defaults to undefined)
28011      */
28012     /**
28013      * @cfg {Boolean} clear
28014      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
28015      */
28016     clear : true,
28017     /**
28018      * @cfg {String} labelSeparator
28019      * The separator to use after field labels (defaults to ':')
28020      */
28021     labelSeparator : ':',
28022     /**
28023      * @cfg {Boolean} hideLabels
28024      * True to suppress the display of field labels in this layout (defaults to false)
28025      */
28026     hideLabels : false,
28027
28028     // private
28029     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
28030     
28031     isLayout : true,
28032     
28033     // private
28034     onRender : function(ct, position){
28035         if(this.el){ // from markup
28036             this.el = Roo.get(this.el);
28037         }else {  // generate
28038             var cfg = this.getAutoCreate();
28039             this.el = ct.createChild(cfg, position);
28040         }
28041         if(this.style){
28042             this.el.applyStyles(this.style);
28043         }
28044         if(this.labelAlign){
28045             this.el.addClass('x-form-label-'+this.labelAlign);
28046         }
28047         if(this.hideLabels){
28048             this.labelStyle = "display:none";
28049             this.elementStyle = "padding-left:0;";
28050         }else{
28051             if(typeof this.labelWidth == 'number'){
28052                 this.labelStyle = "width:"+this.labelWidth+"px;";
28053                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
28054             }
28055             if(this.labelAlign == 'top'){
28056                 this.labelStyle = "width:auto;";
28057                 this.elementStyle = "padding-left:0;";
28058             }
28059         }
28060         var stack = this.stack;
28061         var slen = stack.length;
28062         if(slen > 0){
28063             if(!this.fieldTpl){
28064                 var t = new Roo.Template(
28065                     '<div class="x-form-item {5}">',
28066                         '<label for="{0}" style="{2}">{1}{4}</label>',
28067                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
28068                         '</div>',
28069                     '</div><div class="x-form-clear-left"></div>'
28070                 );
28071                 t.disableFormats = true;
28072                 t.compile();
28073                 Roo.form.Layout.prototype.fieldTpl = t;
28074             }
28075             for(var i = 0; i < slen; i++) {
28076                 if(stack[i].isFormField){
28077                     this.renderField(stack[i]);
28078                 }else{
28079                     this.renderComponent(stack[i]);
28080                 }
28081             }
28082         }
28083         if(this.clear){
28084             this.el.createChild({cls:'x-form-clear'});
28085         }
28086     },
28087
28088     // private
28089     renderField : function(f){
28090         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
28091                f.id, //0
28092                f.fieldLabel, //1
28093                f.labelStyle||this.labelStyle||'', //2
28094                this.elementStyle||'', //3
28095                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
28096                f.itemCls||this.itemCls||''  //5
28097        ], true).getPrevSibling());
28098     },
28099
28100     // private
28101     renderComponent : function(c){
28102         c.render(c.isLayout ? this.el : this.el.createChild());    
28103     },
28104     /**
28105      * Adds a object form elements (using the xtype property as the factory method.)
28106      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
28107      * @param {Object} config 
28108      */
28109     addxtype : function(o)
28110     {
28111         // create the lement.
28112         o.form = this.form;
28113         var fe = Roo.factory(o, Roo.form);
28114         this.form.allItems.push(fe);
28115         this.stack.push(fe);
28116         
28117         if (fe.isFormField) {
28118             this.form.items.add(fe);
28119         }
28120          
28121         return fe;
28122     }
28123 });
28124
28125 /**
28126  * @class Roo.form.Column
28127  * @extends Roo.form.Layout
28128  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
28129  * @constructor
28130  * @param {Object} config Configuration options
28131  */
28132 Roo.form.Column = function(config){
28133     Roo.form.Column.superclass.constructor.call(this, config);
28134 };
28135
28136 Roo.extend(Roo.form.Column, Roo.form.Layout, {
28137     /**
28138      * @cfg {Number/String} width
28139      * The fixed width of the column in pixels or CSS value (defaults to "auto")
28140      */
28141     /**
28142      * @cfg {String/Object} autoCreate
28143      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
28144      */
28145
28146     // private
28147     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
28148
28149     // private
28150     onRender : function(ct, position){
28151         Roo.form.Column.superclass.onRender.call(this, ct, position);
28152         if(this.width){
28153             this.el.setWidth(this.width);
28154         }
28155     }
28156 });
28157
28158
28159 /**
28160  * @class Roo.form.Row
28161  * @extends Roo.form.Layout
28162  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
28163  * @constructor
28164  * @param {Object} config Configuration options
28165  */
28166
28167  
28168 Roo.form.Row = function(config){
28169     Roo.form.Row.superclass.constructor.call(this, config);
28170 };
28171  
28172 Roo.extend(Roo.form.Row, Roo.form.Layout, {
28173       /**
28174      * @cfg {Number/String} width
28175      * The fixed width of the column in pixels or CSS value (defaults to "auto")
28176      */
28177     /**
28178      * @cfg {Number/String} height
28179      * The fixed height of the column in pixels or CSS value (defaults to "auto")
28180      */
28181     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
28182     
28183     padWidth : 20,
28184     // private
28185     onRender : function(ct, position){
28186         //console.log('row render');
28187         if(!this.rowTpl){
28188             var t = new Roo.Template(
28189                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
28190                     '<label for="{0}" style="{2}">{1}{4}</label>',
28191                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
28192                     '</div>',
28193                 '</div>'
28194             );
28195             t.disableFormats = true;
28196             t.compile();
28197             Roo.form.Layout.prototype.rowTpl = t;
28198         }
28199         this.fieldTpl = this.rowTpl;
28200         
28201         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
28202         var labelWidth = 100;
28203         
28204         if ((this.labelAlign != 'top')) {
28205             if (typeof this.labelWidth == 'number') {
28206                 labelWidth = this.labelWidth
28207             }
28208             this.padWidth =  20 + labelWidth;
28209             
28210         }
28211         
28212         Roo.form.Column.superclass.onRender.call(this, ct, position);
28213         if(this.width){
28214             this.el.setWidth(this.width);
28215         }
28216         if(this.height){
28217             this.el.setHeight(this.height);
28218         }
28219     },
28220     
28221     // private
28222     renderField : function(f){
28223         f.fieldEl = this.fieldTpl.append(this.el, [
28224                f.id, f.fieldLabel,
28225                f.labelStyle||this.labelStyle||'',
28226                this.elementStyle||'',
28227                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
28228                f.itemCls||this.itemCls||'',
28229                f.width ? f.width + this.padWidth : 160 + this.padWidth
28230        ],true);
28231     }
28232 });
28233  
28234
28235 /**
28236  * @class Roo.form.FieldSet
28237  * @extends Roo.form.Layout
28238  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
28239  * @constructor
28240  * @param {Object} config Configuration options
28241  */
28242 Roo.form.FieldSet = function(config){
28243     Roo.form.FieldSet.superclass.constructor.call(this, config);
28244 };
28245
28246 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
28247     /**
28248      * @cfg {String} legend
28249      * The text to display as the legend for the FieldSet (defaults to '')
28250      */
28251     /**
28252      * @cfg {String/Object} autoCreate
28253      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
28254      */
28255
28256     // private
28257     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
28258
28259     // private
28260     onRender : function(ct, position){
28261         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
28262         if(this.legend){
28263             this.setLegend(this.legend);
28264         }
28265     },
28266
28267     // private
28268     setLegend : function(text){
28269         if(this.rendered){
28270             this.el.child('legend').update(text);
28271         }
28272     }
28273 });/*
28274  * Based on:
28275  * Ext JS Library 1.1.1
28276  * Copyright(c) 2006-2007, Ext JS, LLC.
28277  *
28278  * Originally Released Under LGPL - original licence link has changed is not relivant.
28279  *
28280  * Fork - LGPL
28281  * <script type="text/javascript">
28282  */
28283 /**
28284  * @class Roo.form.VTypes
28285  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
28286  * @singleton
28287  */
28288 Roo.form.VTypes = function(){
28289     // closure these in so they are only created once.
28290     var alpha = /^[a-zA-Z_]+$/;
28291     var alphanum = /^[a-zA-Z0-9_]+$/;
28292     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
28293     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
28294
28295     // All these messages and functions are configurable
28296     return {
28297         /**
28298          * The function used to validate email addresses
28299          * @param {String} value The email address
28300          */
28301         'email' : function(v){
28302             return email.test(v);
28303         },
28304         /**
28305          * The error text to display when the email validation function returns false
28306          * @type String
28307          */
28308         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
28309         /**
28310          * The keystroke filter mask to be applied on email input
28311          * @type RegExp
28312          */
28313         'emailMask' : /[a-z0-9_\.\-@]/i,
28314
28315         /**
28316          * The function used to validate URLs
28317          * @param {String} value The URL
28318          */
28319         'url' : function(v){
28320             return url.test(v);
28321         },
28322         /**
28323          * The error text to display when the url validation function returns false
28324          * @type String
28325          */
28326         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
28327         
28328         /**
28329          * The function used to validate alpha values
28330          * @param {String} value The value
28331          */
28332         'alpha' : function(v){
28333             return alpha.test(v);
28334         },
28335         /**
28336          * The error text to display when the alpha validation function returns false
28337          * @type String
28338          */
28339         'alphaText' : 'This field should only contain letters and _',
28340         /**
28341          * The keystroke filter mask to be applied on alpha input
28342          * @type RegExp
28343          */
28344         'alphaMask' : /[a-z_]/i,
28345
28346         /**
28347          * The function used to validate alphanumeric values
28348          * @param {String} value The value
28349          */
28350         'alphanum' : function(v){
28351             return alphanum.test(v);
28352         },
28353         /**
28354          * The error text to display when the alphanumeric validation function returns false
28355          * @type String
28356          */
28357         'alphanumText' : 'This field should only contain letters, numbers and _',
28358         /**
28359          * The keystroke filter mask to be applied on alphanumeric input
28360          * @type RegExp
28361          */
28362         'alphanumMask' : /[a-z0-9_]/i
28363     };
28364 }();//<script type="text/javascript">
28365
28366 /**
28367  * @class Roo.form.FCKeditor
28368  * @extends Roo.form.TextArea
28369  * Wrapper around the FCKEditor http://www.fckeditor.net
28370  * @constructor
28371  * Creates a new FCKeditor
28372  * @param {Object} config Configuration options
28373  */
28374 Roo.form.FCKeditor = function(config){
28375     Roo.form.FCKeditor.superclass.constructor.call(this, config);
28376     this.addEvents({
28377          /**
28378          * @event editorinit
28379          * Fired when the editor is initialized - you can add extra handlers here..
28380          * @param {FCKeditor} this
28381          * @param {Object} the FCK object.
28382          */
28383         editorinit : true
28384     });
28385     
28386     
28387 };
28388 Roo.form.FCKeditor.editors = { };
28389 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
28390 {
28391     //defaultAutoCreate : {
28392     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
28393     //},
28394     // private
28395     /**
28396      * @cfg {Object} fck options - see fck manual for details.
28397      */
28398     fckconfig : false,
28399     
28400     /**
28401      * @cfg {Object} fck toolbar set (Basic or Default)
28402      */
28403     toolbarSet : 'Basic',
28404     /**
28405      * @cfg {Object} fck BasePath
28406      */ 
28407     basePath : '/fckeditor/',
28408     
28409     
28410     frame : false,
28411     
28412     value : '',
28413     
28414    
28415     onRender : function(ct, position)
28416     {
28417         if(!this.el){
28418             this.defaultAutoCreate = {
28419                 tag: "textarea",
28420                 style:"width:300px;height:60px;",
28421                 autocomplete: "off"
28422             };
28423         }
28424         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
28425         /*
28426         if(this.grow){
28427             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
28428             if(this.preventScrollbars){
28429                 this.el.setStyle("overflow", "hidden");
28430             }
28431             this.el.setHeight(this.growMin);
28432         }
28433         */
28434         //console.log('onrender' + this.getId() );
28435         Roo.form.FCKeditor.editors[this.getId()] = this;
28436          
28437
28438         this.replaceTextarea() ;
28439         
28440     },
28441     
28442     getEditor : function() {
28443         return this.fckEditor;
28444     },
28445     /**
28446      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
28447      * @param {Mixed} value The value to set
28448      */
28449     
28450     
28451     setValue : function(value)
28452     {
28453         //console.log('setValue: ' + value);
28454         
28455         if(typeof(value) == 'undefined') { // not sure why this is happending...
28456             return;
28457         }
28458         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
28459         
28460         //if(!this.el || !this.getEditor()) {
28461         //    this.value = value;
28462             //this.setValue.defer(100,this,[value]);    
28463         //    return;
28464         //} 
28465         
28466         if(!this.getEditor()) {
28467             return;
28468         }
28469         
28470         this.getEditor().SetData(value);
28471         
28472         //
28473
28474     },
28475
28476     /**
28477      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
28478      * @return {Mixed} value The field value
28479      */
28480     getValue : function()
28481     {
28482         
28483         if (this.frame && this.frame.dom.style.display == 'none') {
28484             return Roo.form.FCKeditor.superclass.getValue.call(this);
28485         }
28486         
28487         if(!this.el || !this.getEditor()) {
28488            
28489            // this.getValue.defer(100,this); 
28490             return this.value;
28491         }
28492        
28493         
28494         var value=this.getEditor().GetData();
28495         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
28496         return Roo.form.FCKeditor.superclass.getValue.call(this);
28497         
28498
28499     },
28500
28501     /**
28502      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
28503      * @return {Mixed} value The field value
28504      */
28505     getRawValue : function()
28506     {
28507         if (this.frame && this.frame.dom.style.display == 'none') {
28508             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
28509         }
28510         
28511         if(!this.el || !this.getEditor()) {
28512             //this.getRawValue.defer(100,this); 
28513             return this.value;
28514             return;
28515         }
28516         
28517         
28518         
28519         var value=this.getEditor().GetData();
28520         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
28521         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
28522          
28523     },
28524     
28525     setSize : function(w,h) {
28526         
28527         
28528         
28529         //if (this.frame && this.frame.dom.style.display == 'none') {
28530         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
28531         //    return;
28532         //}
28533         //if(!this.el || !this.getEditor()) {
28534         //    this.setSize.defer(100,this, [w,h]); 
28535         //    return;
28536         //}
28537         
28538         
28539         
28540         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
28541         
28542         this.frame.dom.setAttribute('width', w);
28543         this.frame.dom.setAttribute('height', h);
28544         this.frame.setSize(w,h);
28545         
28546     },
28547     
28548     toggleSourceEdit : function(value) {
28549         
28550       
28551          
28552         this.el.dom.style.display = value ? '' : 'none';
28553         this.frame.dom.style.display = value ?  'none' : '';
28554         
28555     },
28556     
28557     
28558     focus: function(tag)
28559     {
28560         if (this.frame.dom.style.display == 'none') {
28561             return Roo.form.FCKeditor.superclass.focus.call(this);
28562         }
28563         if(!this.el || !this.getEditor()) {
28564             this.focus.defer(100,this, [tag]); 
28565             return;
28566         }
28567         
28568         
28569         
28570         
28571         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
28572         this.getEditor().Focus();
28573         if (tgs.length) {
28574             if (!this.getEditor().Selection.GetSelection()) {
28575                 this.focus.defer(100,this, [tag]); 
28576                 return;
28577             }
28578             
28579             
28580             var r = this.getEditor().EditorDocument.createRange();
28581             r.setStart(tgs[0],0);
28582             r.setEnd(tgs[0],0);
28583             this.getEditor().Selection.GetSelection().removeAllRanges();
28584             this.getEditor().Selection.GetSelection().addRange(r);
28585             this.getEditor().Focus();
28586         }
28587         
28588     },
28589     
28590     
28591     
28592     replaceTextarea : function()
28593     {
28594         if ( document.getElementById( this.getId() + '___Frame' ) )
28595             return ;
28596         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
28597         //{
28598             // We must check the elements firstly using the Id and then the name.
28599         var oTextarea = document.getElementById( this.getId() );
28600         
28601         var colElementsByName = document.getElementsByName( this.getId() ) ;
28602          
28603         oTextarea.style.display = 'none' ;
28604
28605         if ( oTextarea.tabIndex ) {            
28606             this.TabIndex = oTextarea.tabIndex ;
28607         }
28608         
28609         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
28610         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
28611         this.frame = Roo.get(this.getId() + '___Frame')
28612     },
28613     
28614     _getConfigHtml : function()
28615     {
28616         var sConfig = '' ;
28617
28618         for ( var o in this.fckconfig ) {
28619             sConfig += sConfig.length > 0  ? '&amp;' : '';
28620             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
28621         }
28622
28623         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
28624     },
28625     
28626     
28627     _getIFrameHtml : function()
28628     {
28629         var sFile = 'fckeditor.html' ;
28630         /* no idea what this is about..
28631         try
28632         {
28633             if ( (/fcksource=true/i).test( window.top.location.search ) )
28634                 sFile = 'fckeditor.original.html' ;
28635         }
28636         catch (e) { 
28637         */
28638
28639         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
28640         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
28641         
28642         
28643         var html = '<iframe id="' + this.getId() +
28644             '___Frame" src="' + sLink +
28645             '" width="' + this.width +
28646             '" height="' + this.height + '"' +
28647             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
28648             ' frameborder="0" scrolling="no"></iframe>' ;
28649
28650         return html ;
28651     },
28652     
28653     _insertHtmlBefore : function( html, element )
28654     {
28655         if ( element.insertAdjacentHTML )       {
28656             // IE
28657             element.insertAdjacentHTML( 'beforeBegin', html ) ;
28658         } else { // Gecko
28659             var oRange = document.createRange() ;
28660             oRange.setStartBefore( element ) ;
28661             var oFragment = oRange.createContextualFragment( html );
28662             element.parentNode.insertBefore( oFragment, element ) ;
28663         }
28664     }
28665     
28666     
28667   
28668     
28669     
28670     
28671     
28672
28673 });
28674
28675 //Roo.reg('fckeditor', Roo.form.FCKeditor);
28676
28677 function FCKeditor_OnComplete(editorInstance){
28678     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
28679     f.fckEditor = editorInstance;
28680     //console.log("loaded");
28681     f.fireEvent('editorinit', f, editorInstance);
28682
28683   
28684
28685  
28686
28687
28688
28689
28690
28691
28692
28693
28694
28695
28696
28697
28698
28699
28700
28701 //<script type="text/javascript">
28702 /**
28703  * @class Roo.form.GridField
28704  * @extends Roo.form.Field
28705  * Embed a grid (or editable grid into a form)
28706  * STATUS ALPHA
28707  * 
28708  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
28709  * it needs 
28710  * xgrid.store = Roo.data.Store
28711  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
28712  * xgrid.store.reader = Roo.data.JsonReader 
28713  * 
28714  * 
28715  * @constructor
28716  * Creates a new GridField
28717  * @param {Object} config Configuration options
28718  */
28719 Roo.form.GridField = function(config){
28720     Roo.form.GridField.superclass.constructor.call(this, config);
28721      
28722 };
28723
28724 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
28725     /**
28726      * @cfg {Number} width  - used to restrict width of grid..
28727      */
28728     width : 100,
28729     /**
28730      * @cfg {Number} height - used to restrict height of grid..
28731      */
28732     height : 50,
28733      /**
28734      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
28735          * 
28736          *}
28737      */
28738     xgrid : false, 
28739     /**
28740      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28741      * {tag: "input", type: "checkbox", autocomplete: "off"})
28742      */
28743    // defaultAutoCreate : { tag: 'div' },
28744     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
28745     /**
28746      * @cfg {String} addTitle Text to include for adding a title.
28747      */
28748     addTitle : false,
28749     //
28750     onResize : function(){
28751         Roo.form.Field.superclass.onResize.apply(this, arguments);
28752     },
28753
28754     initEvents : function(){
28755         // Roo.form.Checkbox.superclass.initEvents.call(this);
28756         // has no events...
28757        
28758     },
28759
28760
28761     getResizeEl : function(){
28762         return this.wrap;
28763     },
28764
28765     getPositionEl : function(){
28766         return this.wrap;
28767     },
28768
28769     // private
28770     onRender : function(ct, position){
28771         
28772         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
28773         var style = this.style;
28774         delete this.style;
28775         
28776         Roo.form.GridField.superclass.onRender.call(this, ct, position);
28777         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
28778         this.viewEl = this.wrap.createChild({ tag: 'div' });
28779         if (style) {
28780             this.viewEl.applyStyles(style);
28781         }
28782         if (this.width) {
28783             this.viewEl.setWidth(this.width);
28784         }
28785         if (this.height) {
28786             this.viewEl.setHeight(this.height);
28787         }
28788         //if(this.inputValue !== undefined){
28789         //this.setValue(this.value);
28790         
28791         
28792         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
28793         
28794         
28795         this.grid.render();
28796         this.grid.getDataSource().on('remove', this.refreshValue, this);
28797         this.grid.getDataSource().on('update', this.refreshValue, this);
28798         this.grid.on('afteredit', this.refreshValue, this);
28799  
28800     },
28801      
28802     
28803     /**
28804      * Sets the value of the item. 
28805      * @param {String} either an object  or a string..
28806      */
28807     setValue : function(v){
28808         //this.value = v;
28809         v = v || []; // empty set..
28810         // this does not seem smart - it really only affects memoryproxy grids..
28811         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
28812             var ds = this.grid.getDataSource();
28813             // assumes a json reader..
28814             var data = {}
28815             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
28816             ds.loadData( data);
28817         }
28818         // clear selection so it does not get stale.
28819         if (this.grid.sm) { 
28820             this.grid.sm.clearSelections();
28821         }
28822         
28823         Roo.form.GridField.superclass.setValue.call(this, v);
28824         this.refreshValue();
28825         // should load data in the grid really....
28826     },
28827     
28828     // private
28829     refreshValue: function() {
28830          var val = [];
28831         this.grid.getDataSource().each(function(r) {
28832             val.push(r.data);
28833         });
28834         this.el.dom.value = Roo.encode(val);
28835     }
28836     
28837      
28838     
28839     
28840 });/*
28841  * Based on:
28842  * Ext JS Library 1.1.1
28843  * Copyright(c) 2006-2007, Ext JS, LLC.
28844  *
28845  * Originally Released Under LGPL - original licence link has changed is not relivant.
28846  *
28847  * Fork - LGPL
28848  * <script type="text/javascript">
28849  */
28850 /**
28851  * @class Roo.form.DisplayField
28852  * @extends Roo.form.Field
28853  * A generic Field to display non-editable data.
28854  * @constructor
28855  * Creates a new Display Field item.
28856  * @param {Object} config Configuration options
28857  */
28858 Roo.form.DisplayField = function(config){
28859     Roo.form.DisplayField.superclass.constructor.call(this, config);
28860     
28861 };
28862
28863 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
28864     inputType:      'hidden',
28865     allowBlank:     true,
28866     readOnly:         true,
28867     
28868  
28869     /**
28870      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
28871      */
28872     focusClass : undefined,
28873     /**
28874      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
28875      */
28876     fieldClass: 'x-form-field',
28877     
28878      /**
28879      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
28880      */
28881     valueRenderer: undefined,
28882     
28883     width: 100,
28884     /**
28885      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28886      * {tag: "input", type: "checkbox", autocomplete: "off"})
28887      */
28888      
28889  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
28890
28891     onResize : function(){
28892         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
28893         
28894     },
28895
28896     initEvents : function(){
28897         // Roo.form.Checkbox.superclass.initEvents.call(this);
28898         // has no events...
28899        
28900     },
28901
28902
28903     getResizeEl : function(){
28904         return this.wrap;
28905     },
28906
28907     getPositionEl : function(){
28908         return this.wrap;
28909     },
28910
28911     // private
28912     onRender : function(ct, position){
28913         
28914         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
28915         //if(this.inputValue !== undefined){
28916         this.wrap = this.el.wrap();
28917         
28918         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
28919         
28920         if (this.bodyStyle) {
28921             this.viewEl.applyStyles(this.bodyStyle);
28922         }
28923         //this.viewEl.setStyle('padding', '2px');
28924         
28925         this.setValue(this.value);
28926         
28927     },
28928 /*
28929     // private
28930     initValue : Roo.emptyFn,
28931
28932   */
28933
28934         // private
28935     onClick : function(){
28936         
28937     },
28938
28939     /**
28940      * Sets the checked state of the checkbox.
28941      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
28942      */
28943     setValue : function(v){
28944         this.value = v;
28945         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
28946         // this might be called before we have a dom element..
28947         if (!this.viewEl) {
28948             return;
28949         }
28950         this.viewEl.dom.innerHTML = html;
28951         Roo.form.DisplayField.superclass.setValue.call(this, v);
28952
28953     }
28954 });/*
28955  * 
28956  * Licence- LGPL
28957  * 
28958  */
28959
28960 /**
28961  * @class Roo.form.DayPicker
28962  * @extends Roo.form.Field
28963  * A Day picker show [M] [T] [W] ....
28964  * @constructor
28965  * Creates a new Day Picker
28966  * @param {Object} config Configuration options
28967  */
28968 Roo.form.DayPicker= function(config){
28969     Roo.form.DayPicker.superclass.constructor.call(this, config);
28970      
28971 };
28972
28973 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
28974     /**
28975      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
28976      */
28977     focusClass : undefined,
28978     /**
28979      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
28980      */
28981     fieldClass: "x-form-field",
28982    
28983     /**
28984      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28985      * {tag: "input", type: "checkbox", autocomplete: "off"})
28986      */
28987     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
28988     
28989    
28990     actionMode : 'viewEl', 
28991     //
28992     // private
28993  
28994     inputType : 'hidden',
28995     
28996      
28997     inputElement: false, // real input element?
28998     basedOn: false, // ????
28999     
29000     isFormField: true, // not sure where this is needed!!!!
29001
29002     onResize : function(){
29003         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
29004         if(!this.boxLabel){
29005             this.el.alignTo(this.wrap, 'c-c');
29006         }
29007     },
29008
29009     initEvents : function(){
29010         Roo.form.Checkbox.superclass.initEvents.call(this);
29011         this.el.on("click", this.onClick,  this);
29012         this.el.on("change", this.onClick,  this);
29013     },
29014
29015
29016     getResizeEl : function(){
29017         return this.wrap;
29018     },
29019
29020     getPositionEl : function(){
29021         return this.wrap;
29022     },
29023
29024     
29025     // private
29026     onRender : function(ct, position){
29027         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
29028        
29029         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
29030         
29031         var r1 = '<table><tr>';
29032         var r2 = '<tr class="x-form-daypick-icons">';
29033         for (var i=0; i < 7; i++) {
29034             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
29035             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
29036         }
29037         
29038         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
29039         viewEl.select('img').on('click', this.onClick, this);
29040         this.viewEl = viewEl;   
29041         
29042         
29043         // this will not work on Chrome!!!
29044         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
29045         this.el.on('propertychange', this.setFromHidden,  this);  //ie
29046         
29047         
29048           
29049
29050     },
29051
29052     // private
29053     initValue : Roo.emptyFn,
29054
29055     /**
29056      * Returns the checked state of the checkbox.
29057      * @return {Boolean} True if checked, else false
29058      */
29059     getValue : function(){
29060         return this.el.dom.value;
29061         
29062     },
29063
29064         // private
29065     onClick : function(e){ 
29066         //this.setChecked(!this.checked);
29067         Roo.get(e.target).toggleClass('x-menu-item-checked');
29068         this.refreshValue();
29069         //if(this.el.dom.checked != this.checked){
29070         //    this.setValue(this.el.dom.checked);
29071        // }
29072     },
29073     
29074     // private
29075     refreshValue : function()
29076     {
29077         var val = '';
29078         this.viewEl.select('img',true).each(function(e,i,n)  {
29079             val += e.is(".x-menu-item-checked") ? String(n) : '';
29080         });
29081         this.setValue(val, true);
29082     },
29083
29084     /**
29085      * Sets the checked state of the checkbox.
29086      * On is always based on a string comparison between inputValue and the param.
29087      * @param {Boolean/String} value - the value to set 
29088      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
29089      */
29090     setValue : function(v,suppressEvent){
29091         if (!this.el.dom) {
29092             return;
29093         }
29094         var old = this.el.dom.value ;
29095         this.el.dom.value = v;
29096         if (suppressEvent) {
29097             return ;
29098         }
29099          
29100         // update display..
29101         this.viewEl.select('img',true).each(function(e,i,n)  {
29102             
29103             var on = e.is(".x-menu-item-checked");
29104             var newv = v.indexOf(String(n)) > -1;
29105             if (on != newv) {
29106                 e.toggleClass('x-menu-item-checked');
29107             }
29108             
29109         });
29110         
29111         
29112         this.fireEvent('change', this, v, old);
29113         
29114         
29115     },
29116    
29117     // handle setting of hidden value by some other method!!?!?
29118     setFromHidden: function()
29119     {
29120         if(!this.el){
29121             return;
29122         }
29123         //console.log("SET FROM HIDDEN");
29124         //alert('setFrom hidden');
29125         this.setValue(this.el.dom.value);
29126     },
29127     
29128     onDestroy : function()
29129     {
29130         if(this.viewEl){
29131             Roo.get(this.viewEl).remove();
29132         }
29133          
29134         Roo.form.DayPicker.superclass.onDestroy.call(this);
29135     }
29136
29137 });/*
29138  * RooJS Library 1.1.1
29139  * Copyright(c) 2008-2011  Alan Knowles
29140  *
29141  * License - LGPL
29142  */
29143  
29144
29145 /**
29146  * @class Roo.form.ComboCheck
29147  * @extends Roo.form.ComboBox
29148  * A combobox for multiple select items.
29149  *
29150  * FIXME - could do with a reset button..
29151  * 
29152  * @constructor
29153  * Create a new ComboCheck
29154  * @param {Object} config Configuration options
29155  */
29156 Roo.form.ComboCheck = function(config){
29157     Roo.form.ComboCheck.superclass.constructor.call(this, config);
29158     // should verify some data...
29159     // like
29160     // hiddenName = required..
29161     // displayField = required
29162     // valudField == required
29163     var req= [ 'hiddenName', 'displayField', 'valueField' ];
29164     var _t = this;
29165     Roo.each(req, function(e) {
29166         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
29167             throw "Roo.form.ComboCheck : missing value for: " + e;
29168         }
29169     });
29170     
29171     
29172 };
29173
29174 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
29175      
29176      
29177     editable : false,
29178      
29179     selectedClass: 'x-menu-item-checked', 
29180     
29181     // private
29182     onRender : function(ct, position){
29183         var _t = this;
29184         
29185         
29186         
29187         if(!this.tpl){
29188             var cls = 'x-combo-list';
29189
29190             
29191             this.tpl =  new Roo.Template({
29192                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
29193                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
29194                    '<span>{' + this.displayField + '}</span>' +
29195                     '</div>' 
29196                 
29197             });
29198         }
29199  
29200         
29201         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
29202         this.view.singleSelect = false;
29203         this.view.multiSelect = true;
29204         this.view.toggleSelect = true;
29205         this.pageTb.add(new Roo.Toolbar.Fill(), {
29206             
29207             text: 'Done',
29208             handler: function()
29209             {
29210                 _t.collapse();
29211             }
29212         });
29213     },
29214     
29215     onViewOver : function(e, t){
29216         // do nothing...
29217         return;
29218         
29219     },
29220     
29221     onViewClick : function(doFocus,index){
29222         return;
29223         
29224     },
29225     select: function () {
29226         //Roo.log("SELECT CALLED");
29227     },
29228      
29229     selectByValue : function(xv, scrollIntoView){
29230         var ar = this.getValueArray();
29231         var sels = [];
29232         
29233         Roo.each(ar, function(v) {
29234             if(v === undefined || v === null){
29235                 return;
29236             }
29237             var r = this.findRecord(this.valueField, v);
29238             if(r){
29239                 sels.push(this.store.indexOf(r))
29240                 
29241             }
29242         },this);
29243         this.view.select(sels);
29244         return false;
29245     },
29246     
29247     
29248     
29249     onSelect : function(record, index){
29250        // Roo.log("onselect Called");
29251        // this is only called by the clear button now..
29252         this.view.clearSelections();
29253         this.setValue('[]');
29254         if (this.value != this.valueBefore) {
29255             this.fireEvent('change', this, this.value, this.valueBefore);
29256         }
29257     },
29258     getValueArray : function()
29259     {
29260         var ar = [] ;
29261         
29262         try {
29263             //Roo.log(this.value);
29264             if (typeof(this.value) == 'undefined') {
29265                 return [];
29266             }
29267             var ar = Roo.decode(this.value);
29268             return  ar instanceof Array ? ar : []; //?? valid?
29269             
29270         } catch(e) {
29271             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
29272             return [];
29273         }
29274          
29275     },
29276     expand : function ()
29277     {
29278         Roo.form.ComboCheck.superclass.expand.call(this);
29279         this.valueBefore = this.value;
29280         
29281
29282     },
29283     
29284     collapse : function(){
29285         Roo.form.ComboCheck.superclass.collapse.call(this);
29286         var sl = this.view.getSelectedIndexes();
29287         var st = this.store;
29288         var nv = [];
29289         var tv = [];
29290         var r;
29291         Roo.each(sl, function(i) {
29292             r = st.getAt(i);
29293             nv.push(r.get(this.valueField));
29294         },this);
29295         this.setValue(Roo.encode(nv));
29296         if (this.value != this.valueBefore) {
29297
29298             this.fireEvent('change', this, this.value, this.valueBefore);
29299         }
29300         
29301     },
29302     
29303     setValue : function(v){
29304         // Roo.log(v);
29305         this.value = v;
29306         
29307         var vals = this.getValueArray();
29308         var tv = [];
29309         Roo.each(vals, function(k) {
29310             var r = this.findRecord(this.valueField, k);
29311             if(r){
29312                 tv.push(r.data[this.displayField]);
29313             }else if(this.valueNotFoundText !== undefined){
29314                 tv.push( this.valueNotFoundText );
29315             }
29316         },this);
29317        // Roo.log(tv);
29318         
29319         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
29320         this.hiddenField.value = v;
29321         this.value = v;
29322     }
29323     
29324 });//<script type="text/javasscript">
29325  
29326
29327 /**
29328  * @class Roo.DDView
29329  * A DnD enabled version of Roo.View.
29330  * @param {Element/String} container The Element in which to create the View.
29331  * @param {String} tpl The template string used to create the markup for each element of the View
29332  * @param {Object} config The configuration properties. These include all the config options of
29333  * {@link Roo.View} plus some specific to this class.<br>
29334  * <p>
29335  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
29336  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
29337  * <p>
29338  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
29339 .x-view-drag-insert-above {
29340         border-top:1px dotted #3366cc;
29341 }
29342 .x-view-drag-insert-below {
29343         border-bottom:1px dotted #3366cc;
29344 }
29345 </code></pre>
29346  * 
29347  */
29348  
29349 Roo.DDView = function(container, tpl, config) {
29350     Roo.DDView.superclass.constructor.apply(this, arguments);
29351     this.getEl().setStyle("outline", "0px none");
29352     this.getEl().unselectable();
29353     if (this.dragGroup) {
29354                 this.setDraggable(this.dragGroup.split(","));
29355     }
29356     if (this.dropGroup) {
29357                 this.setDroppable(this.dropGroup.split(","));
29358     }
29359     if (this.deletable) {
29360         this.setDeletable();
29361     }
29362     this.isDirtyFlag = false;
29363         this.addEvents({
29364                 "drop" : true
29365         });
29366 };
29367
29368 Roo.extend(Roo.DDView, Roo.View, {
29369 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
29370 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
29371 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
29372 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
29373
29374         isFormField: true,
29375
29376         reset: Roo.emptyFn,
29377         
29378         clearInvalid: Roo.form.Field.prototype.clearInvalid,
29379
29380         validate: function() {
29381                 return true;
29382         },
29383         
29384         destroy: function() {
29385                 this.purgeListeners();
29386                 this.getEl.removeAllListeners();
29387                 this.getEl().remove();
29388                 if (this.dragZone) {
29389                         if (this.dragZone.destroy) {
29390                                 this.dragZone.destroy();
29391                         }
29392                 }
29393                 if (this.dropZone) {
29394                         if (this.dropZone.destroy) {
29395                                 this.dropZone.destroy();
29396                         }
29397                 }
29398         },
29399
29400 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
29401         getName: function() {
29402                 return this.name;
29403         },
29404
29405 /**     Loads the View from a JSON string representing the Records to put into the Store. */
29406         setValue: function(v) {
29407                 if (!this.store) {
29408                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
29409                 }
29410                 var data = {};
29411                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
29412                 this.store.proxy = new Roo.data.MemoryProxy(data);
29413                 this.store.load();
29414         },
29415
29416 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
29417         getValue: function() {
29418                 var result = '(';
29419                 this.store.each(function(rec) {
29420                         result += rec.id + ',';
29421                 });
29422                 return result.substr(0, result.length - 1) + ')';
29423         },
29424         
29425         getIds: function() {
29426                 var i = 0, result = new Array(this.store.getCount());
29427                 this.store.each(function(rec) {
29428                         result[i++] = rec.id;
29429                 });
29430                 return result;
29431         },
29432         
29433         isDirty: function() {
29434                 return this.isDirtyFlag;
29435         },
29436
29437 /**
29438  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
29439  *      whole Element becomes the target, and this causes the drop gesture to append.
29440  */
29441     getTargetFromEvent : function(e) {
29442                 var target = e.getTarget();
29443                 while ((target !== null) && (target.parentNode != this.el.dom)) {
29444                 target = target.parentNode;
29445                 }
29446                 if (!target) {
29447                         target = this.el.dom.lastChild || this.el.dom;
29448                 }
29449                 return target;
29450     },
29451
29452 /**
29453  *      Create the drag data which consists of an object which has the property "ddel" as
29454  *      the drag proxy element. 
29455  */
29456     getDragData : function(e) {
29457         var target = this.findItemFromChild(e.getTarget());
29458                 if(target) {
29459                         this.handleSelection(e);
29460                         var selNodes = this.getSelectedNodes();
29461             var dragData = {
29462                 source: this,
29463                 copy: this.copy || (this.allowCopy && e.ctrlKey),
29464                 nodes: selNodes,
29465                 records: []
29466                         };
29467                         var selectedIndices = this.getSelectedIndexes();
29468                         for (var i = 0; i < selectedIndices.length; i++) {
29469                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
29470                         }
29471                         if (selNodes.length == 1) {
29472                                 dragData.ddel = target.cloneNode(true); // the div element
29473                         } else {
29474                                 var div = document.createElement('div'); // create the multi element drag "ghost"
29475                                 div.className = 'multi-proxy';
29476                                 for (var i = 0, len = selNodes.length; i < len; i++) {
29477                                         div.appendChild(selNodes[i].cloneNode(true));
29478                                 }
29479                                 dragData.ddel = div;
29480                         }
29481             //console.log(dragData)
29482             //console.log(dragData.ddel.innerHTML)
29483                         return dragData;
29484                 }
29485         //console.log('nodragData')
29486                 return false;
29487     },
29488     
29489 /**     Specify to which ddGroup items in this DDView may be dragged. */
29490     setDraggable: function(ddGroup) {
29491         if (ddGroup instanceof Array) {
29492                 Roo.each(ddGroup, this.setDraggable, this);
29493                 return;
29494         }
29495         if (this.dragZone) {
29496                 this.dragZone.addToGroup(ddGroup);
29497         } else {
29498                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
29499                                 containerScroll: true,
29500                                 ddGroup: ddGroup 
29501
29502                         });
29503 //                      Draggability implies selection. DragZone's mousedown selects the element.
29504                         if (!this.multiSelect) { this.singleSelect = true; }
29505
29506 //                      Wire the DragZone's handlers up to methods in *this*
29507                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
29508                 }
29509     },
29510
29511 /**     Specify from which ddGroup this DDView accepts drops. */
29512     setDroppable: function(ddGroup) {
29513         if (ddGroup instanceof Array) {
29514                 Roo.each(ddGroup, this.setDroppable, this);
29515                 return;
29516         }
29517         if (this.dropZone) {
29518                 this.dropZone.addToGroup(ddGroup);
29519         } else {
29520                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
29521                                 containerScroll: true,
29522                                 ddGroup: ddGroup
29523                         });
29524
29525 //                      Wire the DropZone's handlers up to methods in *this*
29526                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
29527                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
29528                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
29529                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
29530                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
29531                 }
29532     },
29533
29534 /**     Decide whether to drop above or below a View node. */
29535     getDropPoint : function(e, n, dd){
29536         if (n == this.el.dom) { return "above"; }
29537                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
29538                 var c = t + (b - t) / 2;
29539                 var y = Roo.lib.Event.getPageY(e);
29540                 if(y <= c) {
29541                         return "above";
29542                 }else{
29543                         return "below";
29544                 }
29545     },
29546
29547     onNodeEnter : function(n, dd, e, data){
29548                 return false;
29549     },
29550     
29551     onNodeOver : function(n, dd, e, data){
29552                 var pt = this.getDropPoint(e, n, dd);
29553                 // set the insert point style on the target node
29554                 var dragElClass = this.dropNotAllowed;
29555                 if (pt) {
29556                         var targetElClass;
29557                         if (pt == "above"){
29558                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
29559                                 targetElClass = "x-view-drag-insert-above";
29560                         } else {
29561                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
29562                                 targetElClass = "x-view-drag-insert-below";
29563                         }
29564                         if (this.lastInsertClass != targetElClass){
29565                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
29566                                 this.lastInsertClass = targetElClass;
29567                         }
29568                 }
29569                 return dragElClass;
29570         },
29571
29572     onNodeOut : function(n, dd, e, data){
29573                 this.removeDropIndicators(n);
29574     },
29575
29576     onNodeDrop : function(n, dd, e, data){
29577         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
29578                 return false;
29579         }
29580         var pt = this.getDropPoint(e, n, dd);
29581                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
29582                 if (pt == "below") { insertAt++; }
29583                 for (var i = 0; i < data.records.length; i++) {
29584                         var r = data.records[i];
29585                         var dup = this.store.getById(r.id);
29586                         if (dup && (dd != this.dragZone)) {
29587                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
29588                         } else {
29589                                 if (data.copy) {
29590                                         this.store.insert(insertAt++, r.copy());
29591                                 } else {
29592                                         data.source.isDirtyFlag = true;
29593                                         r.store.remove(r);
29594                                         this.store.insert(insertAt++, r);
29595                                 }
29596                                 this.isDirtyFlag = true;
29597                         }
29598                 }
29599                 this.dragZone.cachedTarget = null;
29600                 return true;
29601     },
29602
29603     removeDropIndicators : function(n){
29604                 if(n){
29605                         Roo.fly(n).removeClass([
29606                                 "x-view-drag-insert-above",
29607                                 "x-view-drag-insert-below"]);
29608                         this.lastInsertClass = "_noclass";
29609                 }
29610     },
29611
29612 /**
29613  *      Utility method. Add a delete option to the DDView's context menu.
29614  *      @param {String} imageUrl The URL of the "delete" icon image.
29615  */
29616         setDeletable: function(imageUrl) {
29617                 if (!this.singleSelect && !this.multiSelect) {
29618                         this.singleSelect = true;
29619                 }
29620                 var c = this.getContextMenu();
29621                 this.contextMenu.on("itemclick", function(item) {
29622                         switch (item.id) {
29623                                 case "delete":
29624                                         this.remove(this.getSelectedIndexes());
29625                                         break;
29626                         }
29627                 }, this);
29628                 this.contextMenu.add({
29629                         icon: imageUrl,
29630                         id: "delete",
29631                         text: 'Delete'
29632                 });
29633         },
29634         
29635 /**     Return the context menu for this DDView. */
29636         getContextMenu: function() {
29637                 if (!this.contextMenu) {
29638 //                      Create the View's context menu
29639                         this.contextMenu = new Roo.menu.Menu({
29640                                 id: this.id + "-contextmenu"
29641                         });
29642                         this.el.on("contextmenu", this.showContextMenu, this);
29643                 }
29644                 return this.contextMenu;
29645         },
29646         
29647         disableContextMenu: function() {
29648                 if (this.contextMenu) {
29649                         this.el.un("contextmenu", this.showContextMenu, this);
29650                 }
29651         },
29652
29653         showContextMenu: function(e, item) {
29654         item = this.findItemFromChild(e.getTarget());
29655                 if (item) {
29656                         e.stopEvent();
29657                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
29658                         this.contextMenu.showAt(e.getXY());
29659             }
29660     },
29661
29662 /**
29663  *      Remove {@link Roo.data.Record}s at the specified indices.
29664  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
29665  */
29666     remove: function(selectedIndices) {
29667                 selectedIndices = [].concat(selectedIndices);
29668                 for (var i = 0; i < selectedIndices.length; i++) {
29669                         var rec = this.store.getAt(selectedIndices[i]);
29670                         this.store.remove(rec);
29671                 }
29672     },
29673
29674 /**
29675  *      Double click fires the event, but also, if this is draggable, and there is only one other
29676  *      related DropZone, it transfers the selected node.
29677  */
29678     onDblClick : function(e){
29679         var item = this.findItemFromChild(e.getTarget());
29680         if(item){
29681             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
29682                 return false;
29683             }
29684             if (this.dragGroup) {
29685                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
29686                     while (targets.indexOf(this.dropZone) > -1) {
29687                             targets.remove(this.dropZone);
29688                                 }
29689                     if (targets.length == 1) {
29690                                         this.dragZone.cachedTarget = null;
29691                         var el = Roo.get(targets[0].getEl());
29692                         var box = el.getBox(true);
29693                         targets[0].onNodeDrop(el.dom, {
29694                                 target: el.dom,
29695                                 xy: [box.x, box.y + box.height - 1]
29696                         }, null, this.getDragData(e));
29697                     }
29698                 }
29699         }
29700     },
29701     
29702     handleSelection: function(e) {
29703                 this.dragZone.cachedTarget = null;
29704         var item = this.findItemFromChild(e.getTarget());
29705         if (!item) {
29706                 this.clearSelections(true);
29707                 return;
29708         }
29709                 if (item && (this.multiSelect || this.singleSelect)){
29710                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
29711                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
29712                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
29713                                 this.unselect(item);
29714                         } else {
29715                                 this.select(item, this.multiSelect && e.ctrlKey);
29716                                 this.lastSelection = item;
29717                         }
29718                 }
29719     },
29720
29721     onItemClick : function(item, index, e){
29722                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
29723                         return false;
29724                 }
29725                 return true;
29726     },
29727
29728     unselect : function(nodeInfo, suppressEvent){
29729                 var node = this.getNode(nodeInfo);
29730                 if(node && this.isSelected(node)){
29731                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
29732                                 Roo.fly(node).removeClass(this.selectedClass);
29733                                 this.selections.remove(node);
29734                                 if(!suppressEvent){
29735                                         this.fireEvent("selectionchange", this, this.selections);
29736                                 }
29737                         }
29738                 }
29739     }
29740 });
29741 /*
29742  * Based on:
29743  * Ext JS Library 1.1.1
29744  * Copyright(c) 2006-2007, Ext JS, LLC.
29745  *
29746  * Originally Released Under LGPL - original licence link has changed is not relivant.
29747  *
29748  * Fork - LGPL
29749  * <script type="text/javascript">
29750  */
29751  
29752 /**
29753  * @class Roo.LayoutManager
29754  * @extends Roo.util.Observable
29755  * Base class for layout managers.
29756  */
29757 Roo.LayoutManager = function(container, config){
29758     Roo.LayoutManager.superclass.constructor.call(this);
29759     this.el = Roo.get(container);
29760     // ie scrollbar fix
29761     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
29762         document.body.scroll = "no";
29763     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
29764         this.el.position('relative');
29765     }
29766     this.id = this.el.id;
29767     this.el.addClass("x-layout-container");
29768     /** false to disable window resize monitoring @type Boolean */
29769     this.monitorWindowResize = true;
29770     this.regions = {};
29771     this.addEvents({
29772         /**
29773          * @event layout
29774          * Fires when a layout is performed. 
29775          * @param {Roo.LayoutManager} this
29776          */
29777         "layout" : true,
29778         /**
29779          * @event regionresized
29780          * Fires when the user resizes a region. 
29781          * @param {Roo.LayoutRegion} region The resized region
29782          * @param {Number} newSize The new size (width for east/west, height for north/south)
29783          */
29784         "regionresized" : true,
29785         /**
29786          * @event regioncollapsed
29787          * Fires when a region is collapsed. 
29788          * @param {Roo.LayoutRegion} region The collapsed region
29789          */
29790         "regioncollapsed" : true,
29791         /**
29792          * @event regionexpanded
29793          * Fires when a region is expanded.  
29794          * @param {Roo.LayoutRegion} region The expanded region
29795          */
29796         "regionexpanded" : true
29797     });
29798     this.updating = false;
29799     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
29800 };
29801
29802 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
29803     /**
29804      * Returns true if this layout is currently being updated
29805      * @return {Boolean}
29806      */
29807     isUpdating : function(){
29808         return this.updating; 
29809     },
29810     
29811     /**
29812      * Suspend the LayoutManager from doing auto-layouts while
29813      * making multiple add or remove calls
29814      */
29815     beginUpdate : function(){
29816         this.updating = true;    
29817     },
29818     
29819     /**
29820      * Restore auto-layouts and optionally disable the manager from performing a layout
29821      * @param {Boolean} noLayout true to disable a layout update 
29822      */
29823     endUpdate : function(noLayout){
29824         this.updating = false;
29825         if(!noLayout){
29826             this.layout();
29827         }    
29828     },
29829     
29830     layout: function(){
29831         
29832     },
29833     
29834     onRegionResized : function(region, newSize){
29835         this.fireEvent("regionresized", region, newSize);
29836         this.layout();
29837     },
29838     
29839     onRegionCollapsed : function(region){
29840         this.fireEvent("regioncollapsed", region);
29841     },
29842     
29843     onRegionExpanded : function(region){
29844         this.fireEvent("regionexpanded", region);
29845     },
29846         
29847     /**
29848      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
29849      * performs box-model adjustments.
29850      * @return {Object} The size as an object {width: (the width), height: (the height)}
29851      */
29852     getViewSize : function(){
29853         var size;
29854         if(this.el.dom != document.body){
29855             size = this.el.getSize();
29856         }else{
29857             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
29858         }
29859         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
29860         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
29861         return size;
29862     },
29863     
29864     /**
29865      * Returns the Element this layout is bound to.
29866      * @return {Roo.Element}
29867      */
29868     getEl : function(){
29869         return this.el;
29870     },
29871     
29872     /**
29873      * Returns the specified region.
29874      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
29875      * @return {Roo.LayoutRegion}
29876      */
29877     getRegion : function(target){
29878         return this.regions[target.toLowerCase()];
29879     },
29880     
29881     onWindowResize : function(){
29882         if(this.monitorWindowResize){
29883             this.layout();
29884         }
29885     }
29886 });/*
29887  * Based on:
29888  * Ext JS Library 1.1.1
29889  * Copyright(c) 2006-2007, Ext JS, LLC.
29890  *
29891  * Originally Released Under LGPL - original licence link has changed is not relivant.
29892  *
29893  * Fork - LGPL
29894  * <script type="text/javascript">
29895  */
29896 /**
29897  * @class Roo.BorderLayout
29898  * @extends Roo.LayoutManager
29899  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
29900  * please see: <br><br>
29901  * <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>
29902  * <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>
29903  * Example:
29904  <pre><code>
29905  var layout = new Roo.BorderLayout(document.body, {
29906     north: {
29907         initialSize: 25,
29908         titlebar: false
29909     },
29910     west: {
29911         split:true,
29912         initialSize: 200,
29913         minSize: 175,
29914         maxSize: 400,
29915         titlebar: true,
29916         collapsible: true
29917     },
29918     east: {
29919         split:true,
29920         initialSize: 202,
29921         minSize: 175,
29922         maxSize: 400,
29923         titlebar: true,
29924         collapsible: true
29925     },
29926     south: {
29927         split:true,
29928         initialSize: 100,
29929         minSize: 100,
29930         maxSize: 200,
29931         titlebar: true,
29932         collapsible: true
29933     },
29934     center: {
29935         titlebar: true,
29936         autoScroll:true,
29937         resizeTabs: true,
29938         minTabWidth: 50,
29939         preferredTabWidth: 150
29940     }
29941 });
29942
29943 // shorthand
29944 var CP = Roo.ContentPanel;
29945
29946 layout.beginUpdate();
29947 layout.add("north", new CP("north", "North"));
29948 layout.add("south", new CP("south", {title: "South", closable: true}));
29949 layout.add("west", new CP("west", {title: "West"}));
29950 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
29951 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
29952 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
29953 layout.getRegion("center").showPanel("center1");
29954 layout.endUpdate();
29955 </code></pre>
29956
29957 <b>The container the layout is rendered into can be either the body element or any other element.
29958 If it is not the body element, the container needs to either be an absolute positioned element,
29959 or you will need to add "position:relative" to the css of the container.  You will also need to specify
29960 the container size if it is not the body element.</b>
29961
29962 * @constructor
29963 * Create a new BorderLayout
29964 * @param {String/HTMLElement/Element} container The container this layout is bound to
29965 * @param {Object} config Configuration options
29966  */
29967 Roo.BorderLayout = function(container, config){
29968     config = config || {};
29969     Roo.BorderLayout.superclass.constructor.call(this, container, config);
29970     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
29971     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
29972         var target = this.factory.validRegions[i];
29973         if(config[target]){
29974             this.addRegion(target, config[target]);
29975         }
29976     }
29977 };
29978
29979 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
29980     /**
29981      * Creates and adds a new region if it doesn't already exist.
29982      * @param {String} target The target region key (north, south, east, west or center).
29983      * @param {Object} config The regions config object
29984      * @return {BorderLayoutRegion} The new region
29985      */
29986     addRegion : function(target, config){
29987         if(!this.regions[target]){
29988             var r = this.factory.create(target, this, config);
29989             this.bindRegion(target, r);
29990         }
29991         return this.regions[target];
29992     },
29993
29994     // private (kinda)
29995     bindRegion : function(name, r){
29996         this.regions[name] = r;
29997         r.on("visibilitychange", this.layout, this);
29998         r.on("paneladded", this.layout, this);
29999         r.on("panelremoved", this.layout, this);
30000         r.on("invalidated", this.layout, this);
30001         r.on("resized", this.onRegionResized, this);
30002         r.on("collapsed", this.onRegionCollapsed, this);
30003         r.on("expanded", this.onRegionExpanded, this);
30004     },
30005
30006     /**
30007      * Performs a layout update.
30008      */
30009     layout : function(){
30010         if(this.updating) return;
30011         var size = this.getViewSize();
30012         var w = size.width;
30013         var h = size.height;
30014         var centerW = w;
30015         var centerH = h;
30016         var centerY = 0;
30017         var centerX = 0;
30018         //var x = 0, y = 0;
30019
30020         var rs = this.regions;
30021         var north = rs["north"];
30022         var south = rs["south"]; 
30023         var west = rs["west"];
30024         var east = rs["east"];
30025         var center = rs["center"];
30026         //if(this.hideOnLayout){ // not supported anymore
30027             //c.el.setStyle("display", "none");
30028         //}
30029         if(north && north.isVisible()){
30030             var b = north.getBox();
30031             var m = north.getMargins();
30032             b.width = w - (m.left+m.right);
30033             b.x = m.left;
30034             b.y = m.top;
30035             centerY = b.height + b.y + m.bottom;
30036             centerH -= centerY;
30037             north.updateBox(this.safeBox(b));
30038         }
30039         if(south && south.isVisible()){
30040             var b = south.getBox();
30041             var m = south.getMargins();
30042             b.width = w - (m.left+m.right);
30043             b.x = m.left;
30044             var totalHeight = (b.height + m.top + m.bottom);
30045             b.y = h - totalHeight + m.top;
30046             centerH -= totalHeight;
30047             south.updateBox(this.safeBox(b));
30048         }
30049         if(west && west.isVisible()){
30050             var b = west.getBox();
30051             var m = west.getMargins();
30052             b.height = centerH - (m.top+m.bottom);
30053             b.x = m.left;
30054             b.y = centerY + m.top;
30055             var totalWidth = (b.width + m.left + m.right);
30056             centerX += totalWidth;
30057             centerW -= totalWidth;
30058             west.updateBox(this.safeBox(b));
30059         }
30060         if(east && east.isVisible()){
30061             var b = east.getBox();
30062             var m = east.getMargins();
30063             b.height = centerH - (m.top+m.bottom);
30064             var totalWidth = (b.width + m.left + m.right);
30065             b.x = w - totalWidth + m.left;
30066             b.y = centerY + m.top;
30067             centerW -= totalWidth;
30068             east.updateBox(this.safeBox(b));
30069         }
30070         if(center){
30071             var m = center.getMargins();
30072             var centerBox = {
30073                 x: centerX + m.left,
30074                 y: centerY + m.top,
30075                 width: centerW - (m.left+m.right),
30076                 height: centerH - (m.top+m.bottom)
30077             };
30078             //if(this.hideOnLayout){
30079                 //center.el.setStyle("display", "block");
30080             //}
30081             center.updateBox(this.safeBox(centerBox));
30082         }
30083         this.el.repaint();
30084         this.fireEvent("layout", this);
30085     },
30086
30087     // private
30088     safeBox : function(box){
30089         box.width = Math.max(0, box.width);
30090         box.height = Math.max(0, box.height);
30091         return box;
30092     },
30093
30094     /**
30095      * Adds a ContentPanel (or subclass) to this layout.
30096      * @param {String} target The target region key (north, south, east, west or center).
30097      * @param {Roo.ContentPanel} panel The panel to add
30098      * @return {Roo.ContentPanel} The added panel
30099      */
30100     add : function(target, panel){
30101          
30102         target = target.toLowerCase();
30103         return this.regions[target].add(panel);
30104     },
30105
30106     /**
30107      * Remove a ContentPanel (or subclass) to this layout.
30108      * @param {String} target The target region key (north, south, east, west or center).
30109      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
30110      * @return {Roo.ContentPanel} The removed panel
30111      */
30112     remove : function(target, panel){
30113         target = target.toLowerCase();
30114         return this.regions[target].remove(panel);
30115     },
30116
30117     /**
30118      * Searches all regions for a panel with the specified id
30119      * @param {String} panelId
30120      * @return {Roo.ContentPanel} The panel or null if it wasn't found
30121      */
30122     findPanel : function(panelId){
30123         var rs = this.regions;
30124         for(var target in rs){
30125             if(typeof rs[target] != "function"){
30126                 var p = rs[target].getPanel(panelId);
30127                 if(p){
30128                     return p;
30129                 }
30130             }
30131         }
30132         return null;
30133     },
30134
30135     /**
30136      * Searches all regions for a panel with the specified id and activates (shows) it.
30137      * @param {String/ContentPanel} panelId The panels id or the panel itself
30138      * @return {Roo.ContentPanel} The shown panel or null
30139      */
30140     showPanel : function(panelId) {
30141       var rs = this.regions;
30142       for(var target in rs){
30143          var r = rs[target];
30144          if(typeof r != "function"){
30145             if(r.hasPanel(panelId)){
30146                return r.showPanel(panelId);
30147             }
30148          }
30149       }
30150       return null;
30151    },
30152
30153    /**
30154      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
30155      * @param {Roo.state.Provider} provider (optional) An alternate state provider
30156      */
30157     restoreState : function(provider){
30158         if(!provider){
30159             provider = Roo.state.Manager;
30160         }
30161         var sm = new Roo.LayoutStateManager();
30162         sm.init(this, provider);
30163     },
30164
30165     /**
30166      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
30167      * object should contain properties for each region to add ContentPanels to, and each property's value should be
30168      * a valid ContentPanel config object.  Example:
30169      * <pre><code>
30170 // Create the main layout
30171 var layout = new Roo.BorderLayout('main-ct', {
30172     west: {
30173         split:true,
30174         minSize: 175,
30175         titlebar: true
30176     },
30177     center: {
30178         title:'Components'
30179     }
30180 }, 'main-ct');
30181
30182 // Create and add multiple ContentPanels at once via configs
30183 layout.batchAdd({
30184    west: {
30185        id: 'source-files',
30186        autoCreate:true,
30187        title:'Ext Source Files',
30188        autoScroll:true,
30189        fitToFrame:true
30190    },
30191    center : {
30192        el: cview,
30193        autoScroll:true,
30194        fitToFrame:true,
30195        toolbar: tb,
30196        resizeEl:'cbody'
30197    }
30198 });
30199 </code></pre>
30200      * @param {Object} regions An object containing ContentPanel configs by region name
30201      */
30202     batchAdd : function(regions){
30203         this.beginUpdate();
30204         for(var rname in regions){
30205             var lr = this.regions[rname];
30206             if(lr){
30207                 this.addTypedPanels(lr, regions[rname]);
30208             }
30209         }
30210         this.endUpdate();
30211     },
30212
30213     // private
30214     addTypedPanels : function(lr, ps){
30215         if(typeof ps == 'string'){
30216             lr.add(new Roo.ContentPanel(ps));
30217         }
30218         else if(ps instanceof Array){
30219             for(var i =0, len = ps.length; i < len; i++){
30220                 this.addTypedPanels(lr, ps[i]);
30221             }
30222         }
30223         else if(!ps.events){ // raw config?
30224             var el = ps.el;
30225             delete ps.el; // prevent conflict
30226             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
30227         }
30228         else {  // panel object assumed!
30229             lr.add(ps);
30230         }
30231     },
30232     /**
30233      * Adds a xtype elements to the layout.
30234      * <pre><code>
30235
30236 layout.addxtype({
30237        xtype : 'ContentPanel',
30238        region: 'west',
30239        items: [ .... ]
30240    }
30241 );
30242
30243 layout.addxtype({
30244         xtype : 'NestedLayoutPanel',
30245         region: 'west',
30246         layout: {
30247            center: { },
30248            west: { }   
30249         },
30250         items : [ ... list of content panels or nested layout panels.. ]
30251    }
30252 );
30253 </code></pre>
30254      * @param {Object} cfg Xtype definition of item to add.
30255      */
30256     addxtype : function(cfg)
30257     {
30258         // basically accepts a pannel...
30259         // can accept a layout region..!?!?
30260         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
30261         
30262         if (!cfg.xtype.match(/Panel$/)) {
30263             return false;
30264         }
30265         var ret = false;
30266         
30267         if (typeof(cfg.region) == 'undefined') {
30268             Roo.log("Failed to add Panel, region was not set");
30269             Roo.log(cfg);
30270             return false;
30271         }
30272         var region = cfg.region;
30273         delete cfg.region;
30274         
30275           
30276         var xitems = [];
30277         if (cfg.items) {
30278             xitems = cfg.items;
30279             delete cfg.items;
30280         }
30281         var nb = false;
30282         
30283         switch(cfg.xtype) 
30284         {
30285             case 'ContentPanel':  // ContentPanel (el, cfg)
30286             case 'ScrollPanel':  // ContentPanel (el, cfg)
30287                 if(cfg.autoCreate) {
30288                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
30289                 } else {
30290                     var el = this.el.createChild();
30291                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
30292                 }
30293                 
30294                 this.add(region, ret);
30295                 break;
30296             
30297             
30298             case 'TreePanel': // our new panel!
30299                 cfg.el = this.el.createChild();
30300                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
30301                 this.add(region, ret);
30302                 break;
30303             
30304             case 'NestedLayoutPanel': 
30305                 // create a new Layout (which is  a Border Layout...
30306                 var el = this.el.createChild();
30307                 var clayout = cfg.layout;
30308                 delete cfg.layout;
30309                 clayout.items   = clayout.items  || [];
30310                 // replace this exitems with the clayout ones..
30311                 xitems = clayout.items;
30312                  
30313                 
30314                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
30315                     cfg.background = false;
30316                 }
30317                 var layout = new Roo.BorderLayout(el, clayout);
30318                 
30319                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
30320                 //console.log('adding nested layout panel '  + cfg.toSource());
30321                 this.add(region, ret);
30322                 nb = {}; /// find first...
30323                 break;
30324                 
30325             case 'GridPanel': 
30326             
30327                 // needs grid and region
30328                 
30329                 //var el = this.getRegion(region).el.createChild();
30330                 var el = this.el.createChild();
30331                 // create the grid first...
30332                 
30333                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
30334                 delete cfg.grid;
30335                 if (region == 'center' && this.active ) {
30336                     cfg.background = false;
30337                 }
30338                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
30339                 
30340                 this.add(region, ret);
30341                 if (cfg.background) {
30342                     ret.on('activate', function(gp) {
30343                         if (!gp.grid.rendered) {
30344                             gp.grid.render();
30345                         }
30346                     });
30347                 } else {
30348                     grid.render();
30349                 }
30350                 break;
30351            
30352                
30353                 
30354                 
30355             default: 
30356                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
30357                 return null;
30358              // GridPanel (grid, cfg)
30359             
30360         }
30361         this.beginUpdate();
30362         // add children..
30363         var region = '';
30364         var abn = {};
30365         Roo.each(xitems, function(i)  {
30366             region = nb && i.region ? i.region : false;
30367             
30368             var add = ret.addxtype(i);
30369            
30370             if (region) {
30371                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
30372                 if (!i.background) {
30373                     abn[region] = nb[region] ;
30374                 }
30375             }
30376             
30377         });
30378         this.endUpdate();
30379
30380         // make the last non-background panel active..
30381         //if (nb) { Roo.log(abn); }
30382         if (nb) {
30383             
30384             for(var r in abn) {
30385                 region = this.getRegion(r);
30386                 if (region) {
30387                     // tried using nb[r], but it does not work..
30388                      
30389                     region.showPanel(abn[r]);
30390                    
30391                 }
30392             }
30393         }
30394         return ret;
30395         
30396     }
30397 });
30398
30399 /**
30400  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
30401  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
30402  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
30403  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
30404  * <pre><code>
30405 // shorthand
30406 var CP = Roo.ContentPanel;
30407
30408 var layout = Roo.BorderLayout.create({
30409     north: {
30410         initialSize: 25,
30411         titlebar: false,
30412         panels: [new CP("north", "North")]
30413     },
30414     west: {
30415         split:true,
30416         initialSize: 200,
30417         minSize: 175,
30418         maxSize: 400,
30419         titlebar: true,
30420         collapsible: true,
30421         panels: [new CP("west", {title: "West"})]
30422     },
30423     east: {
30424         split:true,
30425         initialSize: 202,
30426         minSize: 175,
30427         maxSize: 400,
30428         titlebar: true,
30429         collapsible: true,
30430         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
30431     },
30432     south: {
30433         split:true,
30434         initialSize: 100,
30435         minSize: 100,
30436         maxSize: 200,
30437         titlebar: true,
30438         collapsible: true,
30439         panels: [new CP("south", {title: "South", closable: true})]
30440     },
30441     center: {
30442         titlebar: true,
30443         autoScroll:true,
30444         resizeTabs: true,
30445         minTabWidth: 50,
30446         preferredTabWidth: 150,
30447         panels: [
30448             new CP("center1", {title: "Close Me", closable: true}),
30449             new CP("center2", {title: "Center Panel", closable: false})
30450         ]
30451     }
30452 }, document.body);
30453
30454 layout.getRegion("center").showPanel("center1");
30455 </code></pre>
30456  * @param config
30457  * @param targetEl
30458  */
30459 Roo.BorderLayout.create = function(config, targetEl){
30460     var layout = new Roo.BorderLayout(targetEl || document.body, config);
30461     layout.beginUpdate();
30462     var regions = Roo.BorderLayout.RegionFactory.validRegions;
30463     for(var j = 0, jlen = regions.length; j < jlen; j++){
30464         var lr = regions[j];
30465         if(layout.regions[lr] && config[lr].panels){
30466             var r = layout.regions[lr];
30467             var ps = config[lr].panels;
30468             layout.addTypedPanels(r, ps);
30469         }
30470     }
30471     layout.endUpdate();
30472     return layout;
30473 };
30474
30475 // private
30476 Roo.BorderLayout.RegionFactory = {
30477     // private
30478     validRegions : ["north","south","east","west","center"],
30479
30480     // private
30481     create : function(target, mgr, config){
30482         target = target.toLowerCase();
30483         if(config.lightweight || config.basic){
30484             return new Roo.BasicLayoutRegion(mgr, config, target);
30485         }
30486         switch(target){
30487             case "north":
30488                 return new Roo.NorthLayoutRegion(mgr, config);
30489             case "south":
30490                 return new Roo.SouthLayoutRegion(mgr, config);
30491             case "east":
30492                 return new Roo.EastLayoutRegion(mgr, config);
30493             case "west":
30494                 return new Roo.WestLayoutRegion(mgr, config);
30495             case "center":
30496                 return new Roo.CenterLayoutRegion(mgr, config);
30497         }
30498         throw 'Layout region "'+target+'" not supported.';
30499     }
30500 };/*
30501  * Based on:
30502  * Ext JS Library 1.1.1
30503  * Copyright(c) 2006-2007, Ext JS, LLC.
30504  *
30505  * Originally Released Under LGPL - original licence link has changed is not relivant.
30506  *
30507  * Fork - LGPL
30508  * <script type="text/javascript">
30509  */
30510  
30511 /**
30512  * @class Roo.BasicLayoutRegion
30513  * @extends Roo.util.Observable
30514  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
30515  * and does not have a titlebar, tabs or any other features. All it does is size and position 
30516  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
30517  */
30518 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
30519     this.mgr = mgr;
30520     this.position  = pos;
30521     this.events = {
30522         /**
30523          * @scope Roo.BasicLayoutRegion
30524          */
30525         
30526         /**
30527          * @event beforeremove
30528          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
30529          * @param {Roo.LayoutRegion} this
30530          * @param {Roo.ContentPanel} panel The panel
30531          * @param {Object} e The cancel event object
30532          */
30533         "beforeremove" : true,
30534         /**
30535          * @event invalidated
30536          * Fires when the layout for this region is changed.
30537          * @param {Roo.LayoutRegion} this
30538          */
30539         "invalidated" : true,
30540         /**
30541          * @event visibilitychange
30542          * Fires when this region is shown or hidden 
30543          * @param {Roo.LayoutRegion} this
30544          * @param {Boolean} visibility true or false
30545          */
30546         "visibilitychange" : true,
30547         /**
30548          * @event paneladded
30549          * Fires when a panel is added. 
30550          * @param {Roo.LayoutRegion} this
30551          * @param {Roo.ContentPanel} panel The panel
30552          */
30553         "paneladded" : true,
30554         /**
30555          * @event panelremoved
30556          * Fires when a panel is removed. 
30557          * @param {Roo.LayoutRegion} this
30558          * @param {Roo.ContentPanel} panel The panel
30559          */
30560         "panelremoved" : true,
30561         /**
30562          * @event collapsed
30563          * Fires when this region is collapsed.
30564          * @param {Roo.LayoutRegion} this
30565          */
30566         "collapsed" : true,
30567         /**
30568          * @event expanded
30569          * Fires when this region is expanded.
30570          * @param {Roo.LayoutRegion} this
30571          */
30572         "expanded" : true,
30573         /**
30574          * @event slideshow
30575          * Fires when this region is slid into view.
30576          * @param {Roo.LayoutRegion} this
30577          */
30578         "slideshow" : true,
30579         /**
30580          * @event slidehide
30581          * Fires when this region slides out of view. 
30582          * @param {Roo.LayoutRegion} this
30583          */
30584         "slidehide" : true,
30585         /**
30586          * @event panelactivated
30587          * Fires when a panel is activated. 
30588          * @param {Roo.LayoutRegion} this
30589          * @param {Roo.ContentPanel} panel The activated panel
30590          */
30591         "panelactivated" : true,
30592         /**
30593          * @event resized
30594          * Fires when the user resizes this region. 
30595          * @param {Roo.LayoutRegion} this
30596          * @param {Number} newSize The new size (width for east/west, height for north/south)
30597          */
30598         "resized" : true
30599     };
30600     /** A collection of panels in this region. @type Roo.util.MixedCollection */
30601     this.panels = new Roo.util.MixedCollection();
30602     this.panels.getKey = this.getPanelId.createDelegate(this);
30603     this.box = null;
30604     this.activePanel = null;
30605     // ensure listeners are added...
30606     
30607     if (config.listeners || config.events) {
30608         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
30609             listeners : config.listeners || {},
30610             events : config.events || {}
30611         });
30612     }
30613     
30614     if(skipConfig !== true){
30615         this.applyConfig(config);
30616     }
30617 };
30618
30619 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
30620     getPanelId : function(p){
30621         return p.getId();
30622     },
30623     
30624     applyConfig : function(config){
30625         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30626         this.config = config;
30627         
30628     },
30629     
30630     /**
30631      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
30632      * the width, for horizontal (north, south) the height.
30633      * @param {Number} newSize The new width or height
30634      */
30635     resizeTo : function(newSize){
30636         var el = this.el ? this.el :
30637                  (this.activePanel ? this.activePanel.getEl() : null);
30638         if(el){
30639             switch(this.position){
30640                 case "east":
30641                 case "west":
30642                     el.setWidth(newSize);
30643                     this.fireEvent("resized", this, newSize);
30644                 break;
30645                 case "north":
30646                 case "south":
30647                     el.setHeight(newSize);
30648                     this.fireEvent("resized", this, newSize);
30649                 break;                
30650             }
30651         }
30652     },
30653     
30654     getBox : function(){
30655         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
30656     },
30657     
30658     getMargins : function(){
30659         return this.margins;
30660     },
30661     
30662     updateBox : function(box){
30663         this.box = box;
30664         var el = this.activePanel.getEl();
30665         el.dom.style.left = box.x + "px";
30666         el.dom.style.top = box.y + "px";
30667         this.activePanel.setSize(box.width, box.height);
30668     },
30669     
30670     /**
30671      * Returns the container element for this region.
30672      * @return {Roo.Element}
30673      */
30674     getEl : function(){
30675         return this.activePanel;
30676     },
30677     
30678     /**
30679      * Returns true if this region is currently visible.
30680      * @return {Boolean}
30681      */
30682     isVisible : function(){
30683         return this.activePanel ? true : false;
30684     },
30685     
30686     setActivePanel : function(panel){
30687         panel = this.getPanel(panel);
30688         if(this.activePanel && this.activePanel != panel){
30689             this.activePanel.setActiveState(false);
30690             this.activePanel.getEl().setLeftTop(-10000,-10000);
30691         }
30692         this.activePanel = panel;
30693         panel.setActiveState(true);
30694         if(this.box){
30695             panel.setSize(this.box.width, this.box.height);
30696         }
30697         this.fireEvent("panelactivated", this, panel);
30698         this.fireEvent("invalidated");
30699     },
30700     
30701     /**
30702      * Show the specified panel.
30703      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
30704      * @return {Roo.ContentPanel} The shown panel or null
30705      */
30706     showPanel : function(panel){
30707         if(panel = this.getPanel(panel)){
30708             this.setActivePanel(panel);
30709         }
30710         return panel;
30711     },
30712     
30713     /**
30714      * Get the active panel for this region.
30715      * @return {Roo.ContentPanel} The active panel or null
30716      */
30717     getActivePanel : function(){
30718         return this.activePanel;
30719     },
30720     
30721     /**
30722      * Add the passed ContentPanel(s)
30723      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30724      * @return {Roo.ContentPanel} The panel added (if only one was added)
30725      */
30726     add : function(panel){
30727         if(arguments.length > 1){
30728             for(var i = 0, len = arguments.length; i < len; i++) {
30729                 this.add(arguments[i]);
30730             }
30731             return null;
30732         }
30733         if(this.hasPanel(panel)){
30734             this.showPanel(panel);
30735             return panel;
30736         }
30737         var el = panel.getEl();
30738         if(el.dom.parentNode != this.mgr.el.dom){
30739             this.mgr.el.dom.appendChild(el.dom);
30740         }
30741         if(panel.setRegion){
30742             panel.setRegion(this);
30743         }
30744         this.panels.add(panel);
30745         el.setStyle("position", "absolute");
30746         if(!panel.background){
30747             this.setActivePanel(panel);
30748             if(this.config.initialSize && this.panels.getCount()==1){
30749                 this.resizeTo(this.config.initialSize);
30750             }
30751         }
30752         this.fireEvent("paneladded", this, panel);
30753         return panel;
30754     },
30755     
30756     /**
30757      * Returns true if the panel is in this region.
30758      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30759      * @return {Boolean}
30760      */
30761     hasPanel : function(panel){
30762         if(typeof panel == "object"){ // must be panel obj
30763             panel = panel.getId();
30764         }
30765         return this.getPanel(panel) ? true : false;
30766     },
30767     
30768     /**
30769      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30770      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30771      * @param {Boolean} preservePanel Overrides the config preservePanel option
30772      * @return {Roo.ContentPanel} The panel that was removed
30773      */
30774     remove : function(panel, preservePanel){
30775         panel = this.getPanel(panel);
30776         if(!panel){
30777             return null;
30778         }
30779         var e = {};
30780         this.fireEvent("beforeremove", this, panel, e);
30781         if(e.cancel === true){
30782             return null;
30783         }
30784         var panelId = panel.getId();
30785         this.panels.removeKey(panelId);
30786         return panel;
30787     },
30788     
30789     /**
30790      * Returns the panel specified or null if it's not in this region.
30791      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30792      * @return {Roo.ContentPanel}
30793      */
30794     getPanel : function(id){
30795         if(typeof id == "object"){ // must be panel obj
30796             return id;
30797         }
30798         return this.panels.get(id);
30799     },
30800     
30801     /**
30802      * Returns this regions position (north/south/east/west/center).
30803      * @return {String} 
30804      */
30805     getPosition: function(){
30806         return this.position;    
30807     }
30808 });/*
30809  * Based on:
30810  * Ext JS Library 1.1.1
30811  * Copyright(c) 2006-2007, Ext JS, LLC.
30812  *
30813  * Originally Released Under LGPL - original licence link has changed is not relivant.
30814  *
30815  * Fork - LGPL
30816  * <script type="text/javascript">
30817  */
30818  
30819 /**
30820  * @class Roo.LayoutRegion
30821  * @extends Roo.BasicLayoutRegion
30822  * This class represents a region in a layout manager.
30823  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
30824  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
30825  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
30826  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
30827  * @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})
30828  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
30829  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
30830  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
30831  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
30832  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
30833  * @cfg {String}    title           The title for the region (overrides panel titles)
30834  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
30835  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
30836  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
30837  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
30838  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
30839  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
30840  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
30841  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
30842  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
30843  * @cfg {Boolean}   showPin         True to show a pin button
30844  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
30845  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
30846  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
30847  * @cfg {Number}    width           For East/West panels
30848  * @cfg {Number}    height          For North/South panels
30849  * @cfg {Boolean}   split           To show the splitter
30850  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
30851  */
30852 Roo.LayoutRegion = function(mgr, config, pos){
30853     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
30854     var dh = Roo.DomHelper;
30855     /** This region's container element 
30856     * @type Roo.Element */
30857     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
30858     /** This region's title element 
30859     * @type Roo.Element */
30860
30861     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
30862         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
30863         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
30864     ]}, true);
30865     this.titleEl.enableDisplayMode();
30866     /** This region's title text element 
30867     * @type HTMLElement */
30868     this.titleTextEl = this.titleEl.dom.firstChild;
30869     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
30870     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
30871     this.closeBtn.enableDisplayMode();
30872     this.closeBtn.on("click", this.closeClicked, this);
30873     this.closeBtn.hide();
30874
30875     this.createBody(config);
30876     this.visible = true;
30877     this.collapsed = false;
30878
30879     if(config.hideWhenEmpty){
30880         this.hide();
30881         this.on("paneladded", this.validateVisibility, this);
30882         this.on("panelremoved", this.validateVisibility, this);
30883     }
30884     this.applyConfig(config);
30885 };
30886
30887 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
30888
30889     createBody : function(){
30890         /** This region's body element 
30891         * @type Roo.Element */
30892         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
30893     },
30894
30895     applyConfig : function(c){
30896         if(c.collapsible && this.position != "center" && !this.collapsedEl){
30897             var dh = Roo.DomHelper;
30898             if(c.titlebar !== false){
30899                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
30900                 this.collapseBtn.on("click", this.collapse, this);
30901                 this.collapseBtn.enableDisplayMode();
30902
30903                 if(c.showPin === true || this.showPin){
30904                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
30905                     this.stickBtn.enableDisplayMode();
30906                     this.stickBtn.on("click", this.expand, this);
30907                     this.stickBtn.hide();
30908                 }
30909             }
30910             /** This region's collapsed element
30911             * @type Roo.Element */
30912             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
30913                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
30914             ]}, true);
30915             if(c.floatable !== false){
30916                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
30917                this.collapsedEl.on("click", this.collapseClick, this);
30918             }
30919
30920             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
30921                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
30922                    id: "message", unselectable: "on", style:{"float":"left"}});
30923                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
30924              }
30925             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
30926             this.expandBtn.on("click", this.expand, this);
30927         }
30928         if(this.collapseBtn){
30929             this.collapseBtn.setVisible(c.collapsible == true);
30930         }
30931         this.cmargins = c.cmargins || this.cmargins ||
30932                          (this.position == "west" || this.position == "east" ?
30933                              {top: 0, left: 2, right:2, bottom: 0} :
30934                              {top: 2, left: 0, right:0, bottom: 2});
30935         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30936         this.bottomTabs = c.tabPosition != "top";
30937         this.autoScroll = c.autoScroll || false;
30938         if(this.autoScroll){
30939             this.bodyEl.setStyle("overflow", "auto");
30940         }else{
30941             this.bodyEl.setStyle("overflow", "hidden");
30942         }
30943         //if(c.titlebar !== false){
30944             if((!c.titlebar && !c.title) || c.titlebar === false){
30945                 this.titleEl.hide();
30946             }else{
30947                 this.titleEl.show();
30948                 if(c.title){
30949                     this.titleTextEl.innerHTML = c.title;
30950                 }
30951             }
30952         //}
30953         this.duration = c.duration || .30;
30954         this.slideDuration = c.slideDuration || .45;
30955         this.config = c;
30956         if(c.collapsed){
30957             this.collapse(true);
30958         }
30959         if(c.hidden){
30960             this.hide();
30961         }
30962     },
30963     /**
30964      * Returns true if this region is currently visible.
30965      * @return {Boolean}
30966      */
30967     isVisible : function(){
30968         return this.visible;
30969     },
30970
30971     /**
30972      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
30973      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
30974      */
30975     setCollapsedTitle : function(title){
30976         title = title || "&#160;";
30977         if(this.collapsedTitleTextEl){
30978             this.collapsedTitleTextEl.innerHTML = title;
30979         }
30980     },
30981
30982     getBox : function(){
30983         var b;
30984         if(!this.collapsed){
30985             b = this.el.getBox(false, true);
30986         }else{
30987             b = this.collapsedEl.getBox(false, true);
30988         }
30989         return b;
30990     },
30991
30992     getMargins : function(){
30993         return this.collapsed ? this.cmargins : this.margins;
30994     },
30995
30996     highlight : function(){
30997         this.el.addClass("x-layout-panel-dragover");
30998     },
30999
31000     unhighlight : function(){
31001         this.el.removeClass("x-layout-panel-dragover");
31002     },
31003
31004     updateBox : function(box){
31005         this.box = box;
31006         if(!this.collapsed){
31007             this.el.dom.style.left = box.x + "px";
31008             this.el.dom.style.top = box.y + "px";
31009             this.updateBody(box.width, box.height);
31010         }else{
31011             this.collapsedEl.dom.style.left = box.x + "px";
31012             this.collapsedEl.dom.style.top = box.y + "px";
31013             this.collapsedEl.setSize(box.width, box.height);
31014         }
31015         if(this.tabs){
31016             this.tabs.autoSizeTabs();
31017         }
31018     },
31019
31020     updateBody : function(w, h){
31021         if(w !== null){
31022             this.el.setWidth(w);
31023             w -= this.el.getBorderWidth("rl");
31024             if(this.config.adjustments){
31025                 w += this.config.adjustments[0];
31026             }
31027         }
31028         if(h !== null){
31029             this.el.setHeight(h);
31030             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
31031             h -= this.el.getBorderWidth("tb");
31032             if(this.config.adjustments){
31033                 h += this.config.adjustments[1];
31034             }
31035             this.bodyEl.setHeight(h);
31036             if(this.tabs){
31037                 h = this.tabs.syncHeight(h);
31038             }
31039         }
31040         if(this.panelSize){
31041             w = w !== null ? w : this.panelSize.width;
31042             h = h !== null ? h : this.panelSize.height;
31043         }
31044         if(this.activePanel){
31045             var el = this.activePanel.getEl();
31046             w = w !== null ? w : el.getWidth();
31047             h = h !== null ? h : el.getHeight();
31048             this.panelSize = {width: w, height: h};
31049             this.activePanel.setSize(w, h);
31050         }
31051         if(Roo.isIE && this.tabs){
31052             this.tabs.el.repaint();
31053         }
31054     },
31055
31056     /**
31057      * Returns the container element for this region.
31058      * @return {Roo.Element}
31059      */
31060     getEl : function(){
31061         return this.el;
31062     },
31063
31064     /**
31065      * Hides this region.
31066      */
31067     hide : function(){
31068         if(!this.collapsed){
31069             this.el.dom.style.left = "-2000px";
31070             this.el.hide();
31071         }else{
31072             this.collapsedEl.dom.style.left = "-2000px";
31073             this.collapsedEl.hide();
31074         }
31075         this.visible = false;
31076         this.fireEvent("visibilitychange", this, false);
31077     },
31078
31079     /**
31080      * Shows this region if it was previously hidden.
31081      */
31082     show : function(){
31083         if(!this.collapsed){
31084             this.el.show();
31085         }else{
31086             this.collapsedEl.show();
31087         }
31088         this.visible = true;
31089         this.fireEvent("visibilitychange", this, true);
31090     },
31091
31092     closeClicked : function(){
31093         if(this.activePanel){
31094             this.remove(this.activePanel);
31095         }
31096     },
31097
31098     collapseClick : function(e){
31099         if(this.isSlid){
31100            e.stopPropagation();
31101            this.slideIn();
31102         }else{
31103            e.stopPropagation();
31104            this.slideOut();
31105         }
31106     },
31107
31108     /**
31109      * Collapses this region.
31110      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
31111      */
31112     collapse : function(skipAnim){
31113         if(this.collapsed) return;
31114         this.collapsed = true;
31115         if(this.split){
31116             this.split.el.hide();
31117         }
31118         if(this.config.animate && skipAnim !== true){
31119             this.fireEvent("invalidated", this);
31120             this.animateCollapse();
31121         }else{
31122             this.el.setLocation(-20000,-20000);
31123             this.el.hide();
31124             this.collapsedEl.show();
31125             this.fireEvent("collapsed", this);
31126             this.fireEvent("invalidated", this);
31127         }
31128     },
31129
31130     animateCollapse : function(){
31131         // overridden
31132     },
31133
31134     /**
31135      * Expands this region if it was previously collapsed.
31136      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
31137      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
31138      */
31139     expand : function(e, skipAnim){
31140         if(e) e.stopPropagation();
31141         if(!this.collapsed || this.el.hasActiveFx()) return;
31142         if(this.isSlid){
31143             this.afterSlideIn();
31144             skipAnim = true;
31145         }
31146         this.collapsed = false;
31147         if(this.config.animate && skipAnim !== true){
31148             this.animateExpand();
31149         }else{
31150             this.el.show();
31151             if(this.split){
31152                 this.split.el.show();
31153             }
31154             this.collapsedEl.setLocation(-2000,-2000);
31155             this.collapsedEl.hide();
31156             this.fireEvent("invalidated", this);
31157             this.fireEvent("expanded", this);
31158         }
31159     },
31160
31161     animateExpand : function(){
31162         // overridden
31163     },
31164
31165     initTabs : function()
31166     {
31167         this.bodyEl.setStyle("overflow", "hidden");
31168         var ts = new Roo.TabPanel(
31169                 this.bodyEl.dom,
31170                 {
31171                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
31172                     disableTooltips: this.config.disableTabTips,
31173                     toolbar : this.config.toolbar
31174                 }
31175         );
31176         if(this.config.hideTabs){
31177             ts.stripWrap.setDisplayed(false);
31178         }
31179         this.tabs = ts;
31180         ts.resizeTabs = this.config.resizeTabs === true;
31181         ts.minTabWidth = this.config.minTabWidth || 40;
31182         ts.maxTabWidth = this.config.maxTabWidth || 250;
31183         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
31184         ts.monitorResize = false;
31185         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
31186         ts.bodyEl.addClass('x-layout-tabs-body');
31187         this.panels.each(this.initPanelAsTab, this);
31188     },
31189
31190     initPanelAsTab : function(panel){
31191         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
31192                     this.config.closeOnTab && panel.isClosable());
31193         if(panel.tabTip !== undefined){
31194             ti.setTooltip(panel.tabTip);
31195         }
31196         ti.on("activate", function(){
31197               this.setActivePanel(panel);
31198         }, this);
31199         if(this.config.closeOnTab){
31200             ti.on("beforeclose", function(t, e){
31201                 e.cancel = true;
31202                 this.remove(panel);
31203             }, this);
31204         }
31205         return ti;
31206     },
31207
31208     updatePanelTitle : function(panel, title){
31209         if(this.activePanel == panel){
31210             this.updateTitle(title);
31211         }
31212         if(this.tabs){
31213             var ti = this.tabs.getTab(panel.getEl().id);
31214             ti.setText(title);
31215             if(panel.tabTip !== undefined){
31216                 ti.setTooltip(panel.tabTip);
31217             }
31218         }
31219     },
31220
31221     updateTitle : function(title){
31222         if(this.titleTextEl && !this.config.title){
31223             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
31224         }
31225     },
31226
31227     setActivePanel : function(panel){
31228         panel = this.getPanel(panel);
31229         if(this.activePanel && this.activePanel != panel){
31230             this.activePanel.setActiveState(false);
31231         }
31232         this.activePanel = panel;
31233         panel.setActiveState(true);
31234         if(this.panelSize){
31235             panel.setSize(this.panelSize.width, this.panelSize.height);
31236         }
31237         if(this.closeBtn){
31238             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
31239         }
31240         this.updateTitle(panel.getTitle());
31241         if(this.tabs){
31242             this.fireEvent("invalidated", this);
31243         }
31244         this.fireEvent("panelactivated", this, panel);
31245     },
31246
31247     /**
31248      * Shows the specified panel.
31249      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
31250      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
31251      */
31252     showPanel : function(panel){
31253         if(panel = this.getPanel(panel)){
31254             if(this.tabs){
31255                 var tab = this.tabs.getTab(panel.getEl().id);
31256                 if(tab.isHidden()){
31257                     this.tabs.unhideTab(tab.id);
31258                 }
31259                 tab.activate();
31260             }else{
31261                 this.setActivePanel(panel);
31262             }
31263         }
31264         return panel;
31265     },
31266
31267     /**
31268      * Get the active panel for this region.
31269      * @return {Roo.ContentPanel} The active panel or null
31270      */
31271     getActivePanel : function(){
31272         return this.activePanel;
31273     },
31274
31275     validateVisibility : function(){
31276         if(this.panels.getCount() < 1){
31277             this.updateTitle("&#160;");
31278             this.closeBtn.hide();
31279             this.hide();
31280         }else{
31281             if(!this.isVisible()){
31282                 this.show();
31283             }
31284         }
31285     },
31286
31287     /**
31288      * Adds the passed ContentPanel(s) to this region.
31289      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
31290      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
31291      */
31292     add : function(panel){
31293         if(arguments.length > 1){
31294             for(var i = 0, len = arguments.length; i < len; i++) {
31295                 this.add(arguments[i]);
31296             }
31297             return null;
31298         }
31299         if(this.hasPanel(panel)){
31300             this.showPanel(panel);
31301             return panel;
31302         }
31303         panel.setRegion(this);
31304         this.panels.add(panel);
31305         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
31306             this.bodyEl.dom.appendChild(panel.getEl().dom);
31307             if(panel.background !== true){
31308                 this.setActivePanel(panel);
31309             }
31310             this.fireEvent("paneladded", this, panel);
31311             return panel;
31312         }
31313         if(!this.tabs){
31314             this.initTabs();
31315         }else{
31316             this.initPanelAsTab(panel);
31317         }
31318         if(panel.background !== true){
31319             this.tabs.activate(panel.getEl().id);
31320         }
31321         this.fireEvent("paneladded", this, panel);
31322         return panel;
31323     },
31324
31325     /**
31326      * Hides the tab for the specified panel.
31327      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
31328      */
31329     hidePanel : function(panel){
31330         if(this.tabs && (panel = this.getPanel(panel))){
31331             this.tabs.hideTab(panel.getEl().id);
31332         }
31333     },
31334
31335     /**
31336      * Unhides the tab for a previously hidden panel.
31337      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
31338      */
31339     unhidePanel : function(panel){
31340         if(this.tabs && (panel = this.getPanel(panel))){
31341             this.tabs.unhideTab(panel.getEl().id);
31342         }
31343     },
31344
31345     clearPanels : function(){
31346         while(this.panels.getCount() > 0){
31347              this.remove(this.panels.first());
31348         }
31349     },
31350
31351     /**
31352      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
31353      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
31354      * @param {Boolean} preservePanel Overrides the config preservePanel option
31355      * @return {Roo.ContentPanel} The panel that was removed
31356      */
31357     remove : function(panel, preservePanel){
31358         panel = this.getPanel(panel);
31359         if(!panel){
31360             return null;
31361         }
31362         var e = {};
31363         this.fireEvent("beforeremove", this, panel, e);
31364         if(e.cancel === true){
31365             return null;
31366         }
31367         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
31368         var panelId = panel.getId();
31369         this.panels.removeKey(panelId);
31370         if(preservePanel){
31371             document.body.appendChild(panel.getEl().dom);
31372         }
31373         if(this.tabs){
31374             this.tabs.removeTab(panel.getEl().id);
31375         }else if (!preservePanel){
31376             this.bodyEl.dom.removeChild(panel.getEl().dom);
31377         }
31378         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
31379             var p = this.panels.first();
31380             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
31381             tempEl.appendChild(p.getEl().dom);
31382             this.bodyEl.update("");
31383             this.bodyEl.dom.appendChild(p.getEl().dom);
31384             tempEl = null;
31385             this.updateTitle(p.getTitle());
31386             this.tabs = null;
31387             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
31388             this.setActivePanel(p);
31389         }
31390         panel.setRegion(null);
31391         if(this.activePanel == panel){
31392             this.activePanel = null;
31393         }
31394         if(this.config.autoDestroy !== false && preservePanel !== true){
31395             try{panel.destroy();}catch(e){}
31396         }
31397         this.fireEvent("panelremoved", this, panel);
31398         return panel;
31399     },
31400
31401     /**
31402      * Returns the TabPanel component used by this region
31403      * @return {Roo.TabPanel}
31404      */
31405     getTabs : function(){
31406         return this.tabs;
31407     },
31408
31409     createTool : function(parentEl, className){
31410         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
31411             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
31412         btn.addClassOnOver("x-layout-tools-button-over");
31413         return btn;
31414     }
31415 });/*
31416  * Based on:
31417  * Ext JS Library 1.1.1
31418  * Copyright(c) 2006-2007, Ext JS, LLC.
31419  *
31420  * Originally Released Under LGPL - original licence link has changed is not relivant.
31421  *
31422  * Fork - LGPL
31423  * <script type="text/javascript">
31424  */
31425  
31426
31427
31428 /**
31429  * @class Roo.SplitLayoutRegion
31430  * @extends Roo.LayoutRegion
31431  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
31432  */
31433 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
31434     this.cursor = cursor;
31435     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
31436 };
31437
31438 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
31439     splitTip : "Drag to resize.",
31440     collapsibleSplitTip : "Drag to resize. Double click to hide.",
31441     useSplitTips : false,
31442
31443     applyConfig : function(config){
31444         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
31445         if(config.split){
31446             if(!this.split){
31447                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
31448                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
31449                 /** The SplitBar for this region 
31450                 * @type Roo.SplitBar */
31451                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
31452                 this.split.on("moved", this.onSplitMove, this);
31453                 this.split.useShim = config.useShim === true;
31454                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
31455                 if(this.useSplitTips){
31456                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
31457                 }
31458                 if(config.collapsible){
31459                     this.split.el.on("dblclick", this.collapse,  this);
31460                 }
31461             }
31462             if(typeof config.minSize != "undefined"){
31463                 this.split.minSize = config.minSize;
31464             }
31465             if(typeof config.maxSize != "undefined"){
31466                 this.split.maxSize = config.maxSize;
31467             }
31468             if(config.hideWhenEmpty || config.hidden || config.collapsed){
31469                 this.hideSplitter();
31470             }
31471         }
31472     },
31473
31474     getHMaxSize : function(){
31475          var cmax = this.config.maxSize || 10000;
31476          var center = this.mgr.getRegion("center");
31477          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
31478     },
31479
31480     getVMaxSize : function(){
31481          var cmax = this.config.maxSize || 10000;
31482          var center = this.mgr.getRegion("center");
31483          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
31484     },
31485
31486     onSplitMove : function(split, newSize){
31487         this.fireEvent("resized", this, newSize);
31488     },
31489     
31490     /** 
31491      * Returns the {@link Roo.SplitBar} for this region.
31492      * @return {Roo.SplitBar}
31493      */
31494     getSplitBar : function(){
31495         return this.split;
31496     },
31497     
31498     hide : function(){
31499         this.hideSplitter();
31500         Roo.SplitLayoutRegion.superclass.hide.call(this);
31501     },
31502
31503     hideSplitter : function(){
31504         if(this.split){
31505             this.split.el.setLocation(-2000,-2000);
31506             this.split.el.hide();
31507         }
31508     },
31509
31510     show : function(){
31511         if(this.split){
31512             this.split.el.show();
31513         }
31514         Roo.SplitLayoutRegion.superclass.show.call(this);
31515     },
31516     
31517     beforeSlide: function(){
31518         if(Roo.isGecko){// firefox overflow auto bug workaround
31519             this.bodyEl.clip();
31520             if(this.tabs) this.tabs.bodyEl.clip();
31521             if(this.activePanel){
31522                 this.activePanel.getEl().clip();
31523                 
31524                 if(this.activePanel.beforeSlide){
31525                     this.activePanel.beforeSlide();
31526                 }
31527             }
31528         }
31529     },
31530     
31531     afterSlide : function(){
31532         if(Roo.isGecko){// firefox overflow auto bug workaround
31533             this.bodyEl.unclip();
31534             if(this.tabs) this.tabs.bodyEl.unclip();
31535             if(this.activePanel){
31536                 this.activePanel.getEl().unclip();
31537                 if(this.activePanel.afterSlide){
31538                     this.activePanel.afterSlide();
31539                 }
31540             }
31541         }
31542     },
31543
31544     initAutoHide : function(){
31545         if(this.autoHide !== false){
31546             if(!this.autoHideHd){
31547                 var st = new Roo.util.DelayedTask(this.slideIn, this);
31548                 this.autoHideHd = {
31549                     "mouseout": function(e){
31550                         if(!e.within(this.el, true)){
31551                             st.delay(500);
31552                         }
31553                     },
31554                     "mouseover" : function(e){
31555                         st.cancel();
31556                     },
31557                     scope : this
31558                 };
31559             }
31560             this.el.on(this.autoHideHd);
31561         }
31562     },
31563
31564     clearAutoHide : function(){
31565         if(this.autoHide !== false){
31566             this.el.un("mouseout", this.autoHideHd.mouseout);
31567             this.el.un("mouseover", this.autoHideHd.mouseover);
31568         }
31569     },
31570
31571     clearMonitor : function(){
31572         Roo.get(document).un("click", this.slideInIf, this);
31573     },
31574
31575     // these names are backwards but not changed for compat
31576     slideOut : function(){
31577         if(this.isSlid || this.el.hasActiveFx()){
31578             return;
31579         }
31580         this.isSlid = true;
31581         if(this.collapseBtn){
31582             this.collapseBtn.hide();
31583         }
31584         this.closeBtnState = this.closeBtn.getStyle('display');
31585         this.closeBtn.hide();
31586         if(this.stickBtn){
31587             this.stickBtn.show();
31588         }
31589         this.el.show();
31590         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
31591         this.beforeSlide();
31592         this.el.setStyle("z-index", 10001);
31593         this.el.slideIn(this.getSlideAnchor(), {
31594             callback: function(){
31595                 this.afterSlide();
31596                 this.initAutoHide();
31597                 Roo.get(document).on("click", this.slideInIf, this);
31598                 this.fireEvent("slideshow", this);
31599             },
31600             scope: this,
31601             block: true
31602         });
31603     },
31604
31605     afterSlideIn : function(){
31606         this.clearAutoHide();
31607         this.isSlid = false;
31608         this.clearMonitor();
31609         this.el.setStyle("z-index", "");
31610         if(this.collapseBtn){
31611             this.collapseBtn.show();
31612         }
31613         this.closeBtn.setStyle('display', this.closeBtnState);
31614         if(this.stickBtn){
31615             this.stickBtn.hide();
31616         }
31617         this.fireEvent("slidehide", this);
31618     },
31619
31620     slideIn : function(cb){
31621         if(!this.isSlid || this.el.hasActiveFx()){
31622             Roo.callback(cb);
31623             return;
31624         }
31625         this.isSlid = false;
31626         this.beforeSlide();
31627         this.el.slideOut(this.getSlideAnchor(), {
31628             callback: function(){
31629                 this.el.setLeftTop(-10000, -10000);
31630                 this.afterSlide();
31631                 this.afterSlideIn();
31632                 Roo.callback(cb);
31633             },
31634             scope: this,
31635             block: true
31636         });
31637     },
31638     
31639     slideInIf : function(e){
31640         if(!e.within(this.el)){
31641             this.slideIn();
31642         }
31643     },
31644
31645     animateCollapse : function(){
31646         this.beforeSlide();
31647         this.el.setStyle("z-index", 20000);
31648         var anchor = this.getSlideAnchor();
31649         this.el.slideOut(anchor, {
31650             callback : function(){
31651                 this.el.setStyle("z-index", "");
31652                 this.collapsedEl.slideIn(anchor, {duration:.3});
31653                 this.afterSlide();
31654                 this.el.setLocation(-10000,-10000);
31655                 this.el.hide();
31656                 this.fireEvent("collapsed", this);
31657             },
31658             scope: this,
31659             block: true
31660         });
31661     },
31662
31663     animateExpand : function(){
31664         this.beforeSlide();
31665         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
31666         this.el.setStyle("z-index", 20000);
31667         this.collapsedEl.hide({
31668             duration:.1
31669         });
31670         this.el.slideIn(this.getSlideAnchor(), {
31671             callback : function(){
31672                 this.el.setStyle("z-index", "");
31673                 this.afterSlide();
31674                 if(this.split){
31675                     this.split.el.show();
31676                 }
31677                 this.fireEvent("invalidated", this);
31678                 this.fireEvent("expanded", this);
31679             },
31680             scope: this,
31681             block: true
31682         });
31683     },
31684
31685     anchors : {
31686         "west" : "left",
31687         "east" : "right",
31688         "north" : "top",
31689         "south" : "bottom"
31690     },
31691
31692     sanchors : {
31693         "west" : "l",
31694         "east" : "r",
31695         "north" : "t",
31696         "south" : "b"
31697     },
31698
31699     canchors : {
31700         "west" : "tl-tr",
31701         "east" : "tr-tl",
31702         "north" : "tl-bl",
31703         "south" : "bl-tl"
31704     },
31705
31706     getAnchor : function(){
31707         return this.anchors[this.position];
31708     },
31709
31710     getCollapseAnchor : function(){
31711         return this.canchors[this.position];
31712     },
31713
31714     getSlideAnchor : function(){
31715         return this.sanchors[this.position];
31716     },
31717
31718     getAlignAdj : function(){
31719         var cm = this.cmargins;
31720         switch(this.position){
31721             case "west":
31722                 return [0, 0];
31723             break;
31724             case "east":
31725                 return [0, 0];
31726             break;
31727             case "north":
31728                 return [0, 0];
31729             break;
31730             case "south":
31731                 return [0, 0];
31732             break;
31733         }
31734     },
31735
31736     getExpandAdj : function(){
31737         var c = this.collapsedEl, cm = this.cmargins;
31738         switch(this.position){
31739             case "west":
31740                 return [-(cm.right+c.getWidth()+cm.left), 0];
31741             break;
31742             case "east":
31743                 return [cm.right+c.getWidth()+cm.left, 0];
31744             break;
31745             case "north":
31746                 return [0, -(cm.top+cm.bottom+c.getHeight())];
31747             break;
31748             case "south":
31749                 return [0, cm.top+cm.bottom+c.getHeight()];
31750             break;
31751         }
31752     }
31753 });/*
31754  * Based on:
31755  * Ext JS Library 1.1.1
31756  * Copyright(c) 2006-2007, Ext JS, LLC.
31757  *
31758  * Originally Released Under LGPL - original licence link has changed is not relivant.
31759  *
31760  * Fork - LGPL
31761  * <script type="text/javascript">
31762  */
31763 /*
31764  * These classes are private internal classes
31765  */
31766 Roo.CenterLayoutRegion = function(mgr, config){
31767     Roo.LayoutRegion.call(this, mgr, config, "center");
31768     this.visible = true;
31769     this.minWidth = config.minWidth || 20;
31770     this.minHeight = config.minHeight || 20;
31771 };
31772
31773 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
31774     hide : function(){
31775         // center panel can't be hidden
31776     },
31777     
31778     show : function(){
31779         // center panel can't be hidden
31780     },
31781     
31782     getMinWidth: function(){
31783         return this.minWidth;
31784     },
31785     
31786     getMinHeight: function(){
31787         return this.minHeight;
31788     }
31789 });
31790
31791
31792 Roo.NorthLayoutRegion = function(mgr, config){
31793     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
31794     if(this.split){
31795         this.split.placement = Roo.SplitBar.TOP;
31796         this.split.orientation = Roo.SplitBar.VERTICAL;
31797         this.split.el.addClass("x-layout-split-v");
31798     }
31799     var size = config.initialSize || config.height;
31800     if(typeof size != "undefined"){
31801         this.el.setHeight(size);
31802     }
31803 };
31804 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
31805     orientation: Roo.SplitBar.VERTICAL,
31806     getBox : function(){
31807         if(this.collapsed){
31808             return this.collapsedEl.getBox();
31809         }
31810         var box = this.el.getBox();
31811         if(this.split){
31812             box.height += this.split.el.getHeight();
31813         }
31814         return box;
31815     },
31816     
31817     updateBox : function(box){
31818         if(this.split && !this.collapsed){
31819             box.height -= this.split.el.getHeight();
31820             this.split.el.setLeft(box.x);
31821             this.split.el.setTop(box.y+box.height);
31822             this.split.el.setWidth(box.width);
31823         }
31824         if(this.collapsed){
31825             this.updateBody(box.width, null);
31826         }
31827         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31828     }
31829 });
31830
31831 Roo.SouthLayoutRegion = function(mgr, config){
31832     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
31833     if(this.split){
31834         this.split.placement = Roo.SplitBar.BOTTOM;
31835         this.split.orientation = Roo.SplitBar.VERTICAL;
31836         this.split.el.addClass("x-layout-split-v");
31837     }
31838     var size = config.initialSize || config.height;
31839     if(typeof size != "undefined"){
31840         this.el.setHeight(size);
31841     }
31842 };
31843 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
31844     orientation: Roo.SplitBar.VERTICAL,
31845     getBox : function(){
31846         if(this.collapsed){
31847             return this.collapsedEl.getBox();
31848         }
31849         var box = this.el.getBox();
31850         if(this.split){
31851             var sh = this.split.el.getHeight();
31852             box.height += sh;
31853             box.y -= sh;
31854         }
31855         return box;
31856     },
31857     
31858     updateBox : function(box){
31859         if(this.split && !this.collapsed){
31860             var sh = this.split.el.getHeight();
31861             box.height -= sh;
31862             box.y += sh;
31863             this.split.el.setLeft(box.x);
31864             this.split.el.setTop(box.y-sh);
31865             this.split.el.setWidth(box.width);
31866         }
31867         if(this.collapsed){
31868             this.updateBody(box.width, null);
31869         }
31870         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31871     }
31872 });
31873
31874 Roo.EastLayoutRegion = function(mgr, config){
31875     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
31876     if(this.split){
31877         this.split.placement = Roo.SplitBar.RIGHT;
31878         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31879         this.split.el.addClass("x-layout-split-h");
31880     }
31881     var size = config.initialSize || config.width;
31882     if(typeof size != "undefined"){
31883         this.el.setWidth(size);
31884     }
31885 };
31886 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
31887     orientation: Roo.SplitBar.HORIZONTAL,
31888     getBox : function(){
31889         if(this.collapsed){
31890             return this.collapsedEl.getBox();
31891         }
31892         var box = this.el.getBox();
31893         if(this.split){
31894             var sw = this.split.el.getWidth();
31895             box.width += sw;
31896             box.x -= sw;
31897         }
31898         return box;
31899     },
31900
31901     updateBox : function(box){
31902         if(this.split && !this.collapsed){
31903             var sw = this.split.el.getWidth();
31904             box.width -= sw;
31905             this.split.el.setLeft(box.x);
31906             this.split.el.setTop(box.y);
31907             this.split.el.setHeight(box.height);
31908             box.x += sw;
31909         }
31910         if(this.collapsed){
31911             this.updateBody(null, box.height);
31912         }
31913         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31914     }
31915 });
31916
31917 Roo.WestLayoutRegion = function(mgr, config){
31918     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
31919     if(this.split){
31920         this.split.placement = Roo.SplitBar.LEFT;
31921         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31922         this.split.el.addClass("x-layout-split-h");
31923     }
31924     var size = config.initialSize || config.width;
31925     if(typeof size != "undefined"){
31926         this.el.setWidth(size);
31927     }
31928 };
31929 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
31930     orientation: Roo.SplitBar.HORIZONTAL,
31931     getBox : function(){
31932         if(this.collapsed){
31933             return this.collapsedEl.getBox();
31934         }
31935         var box = this.el.getBox();
31936         if(this.split){
31937             box.width += this.split.el.getWidth();
31938         }
31939         return box;
31940     },
31941     
31942     updateBox : function(box){
31943         if(this.split && !this.collapsed){
31944             var sw = this.split.el.getWidth();
31945             box.width -= sw;
31946             this.split.el.setLeft(box.x+box.width);
31947             this.split.el.setTop(box.y);
31948             this.split.el.setHeight(box.height);
31949         }
31950         if(this.collapsed){
31951             this.updateBody(null, box.height);
31952         }
31953         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31954     }
31955 });
31956 /*
31957  * Based on:
31958  * Ext JS Library 1.1.1
31959  * Copyright(c) 2006-2007, Ext JS, LLC.
31960  *
31961  * Originally Released Under LGPL - original licence link has changed is not relivant.
31962  *
31963  * Fork - LGPL
31964  * <script type="text/javascript">
31965  */
31966  
31967  
31968 /*
31969  * Private internal class for reading and applying state
31970  */
31971 Roo.LayoutStateManager = function(layout){
31972      // default empty state
31973      this.state = {
31974         north: {},
31975         south: {},
31976         east: {},
31977         west: {}       
31978     };
31979 };
31980
31981 Roo.LayoutStateManager.prototype = {
31982     init : function(layout, provider){
31983         this.provider = provider;
31984         var state = provider.get(layout.id+"-layout-state");
31985         if(state){
31986             var wasUpdating = layout.isUpdating();
31987             if(!wasUpdating){
31988                 layout.beginUpdate();
31989             }
31990             for(var key in state){
31991                 if(typeof state[key] != "function"){
31992                     var rstate = state[key];
31993                     var r = layout.getRegion(key);
31994                     if(r && rstate){
31995                         if(rstate.size){
31996                             r.resizeTo(rstate.size);
31997                         }
31998                         if(rstate.collapsed == true){
31999                             r.collapse(true);
32000                         }else{
32001                             r.expand(null, true);
32002                         }
32003                     }
32004                 }
32005             }
32006             if(!wasUpdating){
32007                 layout.endUpdate();
32008             }
32009             this.state = state; 
32010         }
32011         this.layout = layout;
32012         layout.on("regionresized", this.onRegionResized, this);
32013         layout.on("regioncollapsed", this.onRegionCollapsed, this);
32014         layout.on("regionexpanded", this.onRegionExpanded, this);
32015     },
32016     
32017     storeState : function(){
32018         this.provider.set(this.layout.id+"-layout-state", this.state);
32019     },
32020     
32021     onRegionResized : function(region, newSize){
32022         this.state[region.getPosition()].size = newSize;
32023         this.storeState();
32024     },
32025     
32026     onRegionCollapsed : function(region){
32027         this.state[region.getPosition()].collapsed = true;
32028         this.storeState();
32029     },
32030     
32031     onRegionExpanded : function(region){
32032         this.state[region.getPosition()].collapsed = false;
32033         this.storeState();
32034     }
32035 };/*
32036  * Based on:
32037  * Ext JS Library 1.1.1
32038  * Copyright(c) 2006-2007, Ext JS, LLC.
32039  *
32040  * Originally Released Under LGPL - original licence link has changed is not relivant.
32041  *
32042  * Fork - LGPL
32043  * <script type="text/javascript">
32044  */
32045 /**
32046  * @class Roo.ContentPanel
32047  * @extends Roo.util.Observable
32048  * A basic ContentPanel element.
32049  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
32050  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
32051  * @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
32052  * @cfg {Boolean}   closable      True if the panel can be closed/removed
32053  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
32054  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
32055  * @cfg {Toolbar}   toolbar       A toolbar for this panel
32056  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
32057  * @cfg {String} title          The title for this panel
32058  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
32059  * @cfg {String} url            Calls {@link #setUrl} with this value
32060  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
32061  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
32062  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
32063  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
32064
32065  * @constructor
32066  * Create a new ContentPanel.
32067  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
32068  * @param {String/Object} config A string to set only the title or a config object
32069  * @param {String} content (optional) Set the HTML content for this panel
32070  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
32071  */
32072 Roo.ContentPanel = function(el, config, content){
32073     
32074      
32075     /*
32076     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
32077         config = el;
32078         el = Roo.id();
32079     }
32080     if (config && config.parentLayout) { 
32081         el = config.parentLayout.el.createChild(); 
32082     }
32083     */
32084     if(el.autoCreate){ // xtype is available if this is called from factory
32085         config = el;
32086         el = Roo.id();
32087     }
32088     this.el = Roo.get(el);
32089     if(!this.el && config && config.autoCreate){
32090         if(typeof config.autoCreate == "object"){
32091             if(!config.autoCreate.id){
32092                 config.autoCreate.id = config.id||el;
32093             }
32094             this.el = Roo.DomHelper.append(document.body,
32095                         config.autoCreate, true);
32096         }else{
32097             this.el = Roo.DomHelper.append(document.body,
32098                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
32099         }
32100     }
32101     this.closable = false;
32102     this.loaded = false;
32103     this.active = false;
32104     if(typeof config == "string"){
32105         this.title = config;
32106     }else{
32107         Roo.apply(this, config);
32108     }
32109     
32110     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
32111         this.wrapEl = this.el.wrap();
32112         this.toolbar.container = this.el.insertSibling(false, 'before');
32113         this.toolbar = new Roo.Toolbar(this.toolbar);
32114     }
32115     
32116     
32117     
32118     if(this.resizeEl){
32119         this.resizeEl = Roo.get(this.resizeEl, true);
32120     }else{
32121         this.resizeEl = this.el;
32122     }
32123     this.addEvents({
32124         /**
32125          * @event activate
32126          * Fires when this panel is activated. 
32127          * @param {Roo.ContentPanel} this
32128          */
32129         "activate" : true,
32130         /**
32131          * @event deactivate
32132          * Fires when this panel is activated. 
32133          * @param {Roo.ContentPanel} this
32134          */
32135         "deactivate" : true,
32136
32137         /**
32138          * @event resize
32139          * Fires when this panel is resized if fitToFrame is true.
32140          * @param {Roo.ContentPanel} this
32141          * @param {Number} width The width after any component adjustments
32142          * @param {Number} height The height after any component adjustments
32143          */
32144         "resize" : true,
32145         
32146          /**
32147          * @event render
32148          * Fires when this tab is created
32149          * @param {Roo.ContentPanel} this
32150          */
32151         "render" : true
32152         
32153         
32154         
32155     });
32156     if(this.autoScroll){
32157         this.resizeEl.setStyle("overflow", "auto");
32158     } else {
32159         // fix randome scrolling
32160         this.el.on('scroll', function() {
32161             Roo.log('fix random scolling');
32162             this.scrollTo('top',0); 
32163         });
32164     }
32165     content = content || this.content;
32166     if(content){
32167         this.setContent(content);
32168     }
32169     if(config && config.url){
32170         this.setUrl(this.url, this.params, this.loadOnce);
32171     }
32172     
32173     
32174     
32175     Roo.ContentPanel.superclass.constructor.call(this);
32176     
32177     this.fireEvent('render', this);
32178 };
32179
32180 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
32181     tabTip:'',
32182     setRegion : function(region){
32183         this.region = region;
32184         if(region){
32185            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
32186         }else{
32187            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
32188         } 
32189     },
32190     
32191     /**
32192      * Returns the toolbar for this Panel if one was configured. 
32193      * @return {Roo.Toolbar} 
32194      */
32195     getToolbar : function(){
32196         return this.toolbar;
32197     },
32198     
32199     setActiveState : function(active){
32200         this.active = active;
32201         if(!active){
32202             this.fireEvent("deactivate", this);
32203         }else{
32204             this.fireEvent("activate", this);
32205         }
32206     },
32207     /**
32208      * Updates this panel's element
32209      * @param {String} content The new content
32210      * @param {Boolean} loadScripts (optional) true to look for and process scripts
32211     */
32212     setContent : function(content, loadScripts){
32213         this.el.update(content, loadScripts);
32214     },
32215
32216     ignoreResize : function(w, h){
32217         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
32218             return true;
32219         }else{
32220             this.lastSize = {width: w, height: h};
32221             return false;
32222         }
32223     },
32224     /**
32225      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
32226      * @return {Roo.UpdateManager} The UpdateManager
32227      */
32228     getUpdateManager : function(){
32229         return this.el.getUpdateManager();
32230     },
32231      /**
32232      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
32233      * @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:
32234 <pre><code>
32235 panel.load({
32236     url: "your-url.php",
32237     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
32238     callback: yourFunction,
32239     scope: yourObject, //(optional scope)
32240     discardUrl: false,
32241     nocache: false,
32242     text: "Loading...",
32243     timeout: 30,
32244     scripts: false
32245 });
32246 </code></pre>
32247      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
32248      * 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.
32249      * @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}
32250      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
32251      * @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.
32252      * @return {Roo.ContentPanel} this
32253      */
32254     load : function(){
32255         var um = this.el.getUpdateManager();
32256         um.update.apply(um, arguments);
32257         return this;
32258     },
32259
32260
32261     /**
32262      * 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.
32263      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
32264      * @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)
32265      * @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)
32266      * @return {Roo.UpdateManager} The UpdateManager
32267      */
32268     setUrl : function(url, params, loadOnce){
32269         if(this.refreshDelegate){
32270             this.removeListener("activate", this.refreshDelegate);
32271         }
32272         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
32273         this.on("activate", this.refreshDelegate);
32274         return this.el.getUpdateManager();
32275     },
32276     
32277     _handleRefresh : function(url, params, loadOnce){
32278         if(!loadOnce || !this.loaded){
32279             var updater = this.el.getUpdateManager();
32280             updater.update(url, params, this._setLoaded.createDelegate(this));
32281         }
32282     },
32283     
32284     _setLoaded : function(){
32285         this.loaded = true;
32286     }, 
32287     
32288     /**
32289      * Returns this panel's id
32290      * @return {String} 
32291      */
32292     getId : function(){
32293         return this.el.id;
32294     },
32295     
32296     /** 
32297      * Returns this panel's element - used by regiosn to add.
32298      * @return {Roo.Element} 
32299      */
32300     getEl : function(){
32301         return this.wrapEl || this.el;
32302     },
32303     
32304     adjustForComponents : function(width, height){
32305         if(this.resizeEl != this.el){
32306             width -= this.el.getFrameWidth('lr');
32307             height -= this.el.getFrameWidth('tb');
32308         }
32309         if(this.toolbar){
32310             var te = this.toolbar.getEl();
32311             height -= te.getHeight();
32312             te.setWidth(width);
32313         }
32314         if(this.adjustments){
32315             width += this.adjustments[0];
32316             height += this.adjustments[1];
32317         }
32318         return {"width": width, "height": height};
32319     },
32320     
32321     setSize : function(width, height){
32322         if(this.fitToFrame && !this.ignoreResize(width, height)){
32323             if(this.fitContainer && this.resizeEl != this.el){
32324                 this.el.setSize(width, height);
32325             }
32326             var size = this.adjustForComponents(width, height);
32327             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
32328             this.fireEvent('resize', this, size.width, size.height);
32329         }
32330     },
32331     
32332     /**
32333      * Returns this panel's title
32334      * @return {String} 
32335      */
32336     getTitle : function(){
32337         return this.title;
32338     },
32339     
32340     /**
32341      * Set this panel's title
32342      * @param {String} title
32343      */
32344     setTitle : function(title){
32345         this.title = title;
32346         if(this.region){
32347             this.region.updatePanelTitle(this, title);
32348         }
32349     },
32350     
32351     /**
32352      * Returns true is this panel was configured to be closable
32353      * @return {Boolean} 
32354      */
32355     isClosable : function(){
32356         return this.closable;
32357     },
32358     
32359     beforeSlide : function(){
32360         this.el.clip();
32361         this.resizeEl.clip();
32362     },
32363     
32364     afterSlide : function(){
32365         this.el.unclip();
32366         this.resizeEl.unclip();
32367     },
32368     
32369     /**
32370      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
32371      *   Will fail silently if the {@link #setUrl} method has not been called.
32372      *   This does not activate the panel, just updates its content.
32373      */
32374     refresh : function(){
32375         if(this.refreshDelegate){
32376            this.loaded = false;
32377            this.refreshDelegate();
32378         }
32379     },
32380     
32381     /**
32382      * Destroys this panel
32383      */
32384     destroy : function(){
32385         this.el.removeAllListeners();
32386         var tempEl = document.createElement("span");
32387         tempEl.appendChild(this.el.dom);
32388         tempEl.innerHTML = "";
32389         this.el.remove();
32390         this.el = null;
32391     },
32392     
32393     /**
32394      * form - if the content panel contains a form - this is a reference to it.
32395      * @type {Roo.form.Form}
32396      */
32397     form : false,
32398     /**
32399      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
32400      *    This contains a reference to it.
32401      * @type {Roo.View}
32402      */
32403     view : false,
32404     
32405       /**
32406      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
32407      * <pre><code>
32408
32409 layout.addxtype({
32410        xtype : 'Form',
32411        items: [ .... ]
32412    }
32413 );
32414
32415 </code></pre>
32416      * @param {Object} cfg Xtype definition of item to add.
32417      */
32418     
32419     addxtype : function(cfg) {
32420         // add form..
32421         if (cfg.xtype.match(/^Form$/)) {
32422             var el = this.el.createChild();
32423
32424             this.form = new  Roo.form.Form(cfg);
32425             
32426             
32427             if ( this.form.allItems.length) this.form.render(el.dom);
32428             return this.form;
32429         }
32430         // should only have one of theses..
32431         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
32432             // views..
32433             cfg.el = this.el.appendChild(document.createElement("div"));
32434             // factory?
32435             
32436             var ret = new Roo.factory(cfg);
32437             ret.render && ret.render(false, ''); // render blank..
32438             this.view = ret;
32439             return ret;
32440         }
32441         return false;
32442     }
32443 });
32444
32445 /**
32446  * @class Roo.GridPanel
32447  * @extends Roo.ContentPanel
32448  * @constructor
32449  * Create a new GridPanel.
32450  * @param {Roo.grid.Grid} grid The grid for this panel
32451  * @param {String/Object} config A string to set only the panel's title, or a config object
32452  */
32453 Roo.GridPanel = function(grid, config){
32454     
32455   
32456     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
32457         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
32458         
32459     this.wrapper.dom.appendChild(grid.getGridEl().dom);
32460     
32461     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
32462     
32463     if(this.toolbar){
32464         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
32465     }
32466     // xtype created footer. - not sure if will work as we normally have to render first..
32467     if (this.footer && !this.footer.el && this.footer.xtype) {
32468         
32469         this.footer.container = this.grid.getView().getFooterPanel(true);
32470         this.footer.dataSource = this.grid.dataSource;
32471         this.footer = Roo.factory(this.footer, Roo);
32472         
32473     }
32474     
32475     grid.monitorWindowResize = false; // turn off autosizing
32476     grid.autoHeight = false;
32477     grid.autoWidth = false;
32478     this.grid = grid;
32479     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
32480 };
32481
32482 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
32483     getId : function(){
32484         return this.grid.id;
32485     },
32486     
32487     /**
32488      * Returns the grid for this panel
32489      * @return {Roo.grid.Grid} 
32490      */
32491     getGrid : function(){
32492         return this.grid;    
32493     },
32494     
32495     setSize : function(width, height){
32496         if(!this.ignoreResize(width, height)){
32497             var grid = this.grid;
32498             var size = this.adjustForComponents(width, height);
32499             grid.getGridEl().setSize(size.width, size.height);
32500             grid.autoSize();
32501         }
32502     },
32503     
32504     beforeSlide : function(){
32505         this.grid.getView().scroller.clip();
32506     },
32507     
32508     afterSlide : function(){
32509         this.grid.getView().scroller.unclip();
32510     },
32511     
32512     destroy : function(){
32513         this.grid.destroy();
32514         delete this.grid;
32515         Roo.GridPanel.superclass.destroy.call(this); 
32516     }
32517 });
32518
32519
32520 /**
32521  * @class Roo.NestedLayoutPanel
32522  * @extends Roo.ContentPanel
32523  * @constructor
32524  * Create a new NestedLayoutPanel.
32525  * 
32526  * 
32527  * @param {Roo.BorderLayout} layout The layout for this panel
32528  * @param {String/Object} config A string to set only the title or a config object
32529  */
32530 Roo.NestedLayoutPanel = function(layout, config)
32531 {
32532     // construct with only one argument..
32533     /* FIXME - implement nicer consturctors
32534     if (layout.layout) {
32535         config = layout;
32536         layout = config.layout;
32537         delete config.layout;
32538     }
32539     if (layout.xtype && !layout.getEl) {
32540         // then layout needs constructing..
32541         layout = Roo.factory(layout, Roo);
32542     }
32543     */
32544     
32545     
32546     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
32547     
32548     layout.monitorWindowResize = false; // turn off autosizing
32549     this.layout = layout;
32550     this.layout.getEl().addClass("x-layout-nested-layout");
32551     
32552     
32553     
32554     
32555 };
32556
32557 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
32558
32559     setSize : function(width, height){
32560         if(!this.ignoreResize(width, height)){
32561             var size = this.adjustForComponents(width, height);
32562             var el = this.layout.getEl();
32563             el.setSize(size.width, size.height);
32564             var touch = el.dom.offsetWidth;
32565             this.layout.layout();
32566             // ie requires a double layout on the first pass
32567             if(Roo.isIE && !this.initialized){
32568                 this.initialized = true;
32569                 this.layout.layout();
32570             }
32571         }
32572     },
32573     
32574     // activate all subpanels if not currently active..
32575     
32576     setActiveState : function(active){
32577         this.active = active;
32578         if(!active){
32579             this.fireEvent("deactivate", this);
32580             return;
32581         }
32582         
32583         this.fireEvent("activate", this);
32584         // not sure if this should happen before or after..
32585         if (!this.layout) {
32586             return; // should not happen..
32587         }
32588         var reg = false;
32589         for (var r in this.layout.regions) {
32590             reg = this.layout.getRegion(r);
32591             if (reg.getActivePanel()) {
32592                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
32593                 reg.setActivePanel(reg.getActivePanel());
32594                 continue;
32595             }
32596             if (!reg.panels.length) {
32597                 continue;
32598             }
32599             reg.showPanel(reg.getPanel(0));
32600         }
32601         
32602         
32603         
32604         
32605     },
32606     
32607     /**
32608      * Returns the nested BorderLayout for this panel
32609      * @return {Roo.BorderLayout} 
32610      */
32611     getLayout : function(){
32612         return this.layout;
32613     },
32614     
32615      /**
32616      * Adds a xtype elements to the layout of the nested panel
32617      * <pre><code>
32618
32619 panel.addxtype({
32620        xtype : 'ContentPanel',
32621        region: 'west',
32622        items: [ .... ]
32623    }
32624 );
32625
32626 panel.addxtype({
32627         xtype : 'NestedLayoutPanel',
32628         region: 'west',
32629         layout: {
32630            center: { },
32631            west: { }   
32632         },
32633         items : [ ... list of content panels or nested layout panels.. ]
32634    }
32635 );
32636 </code></pre>
32637      * @param {Object} cfg Xtype definition of item to add.
32638      */
32639     addxtype : function(cfg) {
32640         return this.layout.addxtype(cfg);
32641     
32642     }
32643 });
32644
32645 Roo.ScrollPanel = function(el, config, content){
32646     config = config || {};
32647     config.fitToFrame = true;
32648     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
32649     
32650     this.el.dom.style.overflow = "hidden";
32651     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
32652     this.el.removeClass("x-layout-inactive-content");
32653     this.el.on("mousewheel", this.onWheel, this);
32654
32655     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
32656     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
32657     up.unselectable(); down.unselectable();
32658     up.on("click", this.scrollUp, this);
32659     down.on("click", this.scrollDown, this);
32660     up.addClassOnOver("x-scroller-btn-over");
32661     down.addClassOnOver("x-scroller-btn-over");
32662     up.addClassOnClick("x-scroller-btn-click");
32663     down.addClassOnClick("x-scroller-btn-click");
32664     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
32665
32666     this.resizeEl = this.el;
32667     this.el = wrap; this.up = up; this.down = down;
32668 };
32669
32670 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
32671     increment : 100,
32672     wheelIncrement : 5,
32673     scrollUp : function(){
32674         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
32675     },
32676
32677     scrollDown : function(){
32678         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
32679     },
32680
32681     afterScroll : function(){
32682         var el = this.resizeEl;
32683         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
32684         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
32685         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
32686     },
32687
32688     setSize : function(){
32689         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
32690         this.afterScroll();
32691     },
32692
32693     onWheel : function(e){
32694         var d = e.getWheelDelta();
32695         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
32696         this.afterScroll();
32697         e.stopEvent();
32698     },
32699
32700     setContent : function(content, loadScripts){
32701         this.resizeEl.update(content, loadScripts);
32702     }
32703
32704 });
32705
32706
32707
32708
32709
32710
32711
32712
32713
32714 /**
32715  * @class Roo.TreePanel
32716  * @extends Roo.ContentPanel
32717  * @constructor
32718  * Create a new TreePanel. - defaults to fit/scoll contents.
32719  * @param {String/Object} config A string to set only the panel's title, or a config object
32720  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
32721  */
32722 Roo.TreePanel = function(config){
32723     var el = config.el;
32724     var tree = config.tree;
32725     delete config.tree; 
32726     delete config.el; // hopefull!
32727     
32728     // wrapper for IE7 strict & safari scroll issue
32729     
32730     var treeEl = el.createChild();
32731     config.resizeEl = treeEl;
32732     
32733     
32734     
32735     Roo.TreePanel.superclass.constructor.call(this, el, config);
32736  
32737  
32738     this.tree = new Roo.tree.TreePanel(treeEl , tree);
32739     //console.log(tree);
32740     this.on('activate', function()
32741     {
32742         if (this.tree.rendered) {
32743             return;
32744         }
32745         //console.log('render tree');
32746         this.tree.render();
32747     });
32748     
32749     this.on('resize',  function (cp, w, h) {
32750             this.tree.innerCt.setWidth(w);
32751             this.tree.innerCt.setHeight(h);
32752             this.tree.innerCt.setStyle('overflow-y', 'auto');
32753     });
32754
32755         
32756     
32757 };
32758
32759 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
32760     fitToFrame : true,
32761     autoScroll : true
32762 });
32763
32764
32765
32766
32767
32768
32769
32770
32771
32772
32773
32774 /*
32775  * Based on:
32776  * Ext JS Library 1.1.1
32777  * Copyright(c) 2006-2007, Ext JS, LLC.
32778  *
32779  * Originally Released Under LGPL - original licence link has changed is not relivant.
32780  *
32781  * Fork - LGPL
32782  * <script type="text/javascript">
32783  */
32784  
32785
32786 /**
32787  * @class Roo.ReaderLayout
32788  * @extends Roo.BorderLayout
32789  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
32790  * center region containing two nested regions (a top one for a list view and one for item preview below),
32791  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
32792  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
32793  * expedites the setup of the overall layout and regions for this common application style.
32794  * Example:
32795  <pre><code>
32796 var reader = new Roo.ReaderLayout();
32797 var CP = Roo.ContentPanel;  // shortcut for adding
32798
32799 reader.beginUpdate();
32800 reader.add("north", new CP("north", "North"));
32801 reader.add("west", new CP("west", {title: "West"}));
32802 reader.add("east", new CP("east", {title: "East"}));
32803
32804 reader.regions.listView.add(new CP("listView", "List"));
32805 reader.regions.preview.add(new CP("preview", "Preview"));
32806 reader.endUpdate();
32807 </code></pre>
32808 * @constructor
32809 * Create a new ReaderLayout
32810 * @param {Object} config Configuration options
32811 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
32812 * document.body if omitted)
32813 */
32814 Roo.ReaderLayout = function(config, renderTo){
32815     var c = config || {size:{}};
32816     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
32817         north: c.north !== false ? Roo.apply({
32818             split:false,
32819             initialSize: 32,
32820             titlebar: false
32821         }, c.north) : false,
32822         west: c.west !== false ? Roo.apply({
32823             split:true,
32824             initialSize: 200,
32825             minSize: 175,
32826             maxSize: 400,
32827             titlebar: true,
32828             collapsible: true,
32829             animate: true,
32830             margins:{left:5,right:0,bottom:5,top:5},
32831             cmargins:{left:5,right:5,bottom:5,top:5}
32832         }, c.west) : false,
32833         east: c.east !== false ? Roo.apply({
32834             split:true,
32835             initialSize: 200,
32836             minSize: 175,
32837             maxSize: 400,
32838             titlebar: true,
32839             collapsible: true,
32840             animate: true,
32841             margins:{left:0,right:5,bottom:5,top:5},
32842             cmargins:{left:5,right:5,bottom:5,top:5}
32843         }, c.east) : false,
32844         center: Roo.apply({
32845             tabPosition: 'top',
32846             autoScroll:false,
32847             closeOnTab: true,
32848             titlebar:false,
32849             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
32850         }, c.center)
32851     });
32852
32853     this.el.addClass('x-reader');
32854
32855     this.beginUpdate();
32856
32857     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
32858         south: c.preview !== false ? Roo.apply({
32859             split:true,
32860             initialSize: 200,
32861             minSize: 100,
32862             autoScroll:true,
32863             collapsible:true,
32864             titlebar: true,
32865             cmargins:{top:5,left:0, right:0, bottom:0}
32866         }, c.preview) : false,
32867         center: Roo.apply({
32868             autoScroll:false,
32869             titlebar:false,
32870             minHeight:200
32871         }, c.listView)
32872     });
32873     this.add('center', new Roo.NestedLayoutPanel(inner,
32874             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
32875
32876     this.endUpdate();
32877
32878     this.regions.preview = inner.getRegion('south');
32879     this.regions.listView = inner.getRegion('center');
32880 };
32881
32882 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
32883  * Based on:
32884  * Ext JS Library 1.1.1
32885  * Copyright(c) 2006-2007, Ext JS, LLC.
32886  *
32887  * Originally Released Under LGPL - original licence link has changed is not relivant.
32888  *
32889  * Fork - LGPL
32890  * <script type="text/javascript">
32891  */
32892  
32893 /**
32894  * @class Roo.grid.Grid
32895  * @extends Roo.util.Observable
32896  * This class represents the primary interface of a component based grid control.
32897  * <br><br>Usage:<pre><code>
32898  var grid = new Roo.grid.Grid("my-container-id", {
32899      ds: myDataStore,
32900      cm: myColModel,
32901      selModel: mySelectionModel,
32902      autoSizeColumns: true,
32903      monitorWindowResize: false,
32904      trackMouseOver: true
32905  });
32906  // set any options
32907  grid.render();
32908  * </code></pre>
32909  * <b>Common Problems:</b><br/>
32910  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
32911  * element will correct this<br/>
32912  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
32913  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
32914  * are unpredictable.<br/>
32915  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
32916  * grid to calculate dimensions/offsets.<br/>
32917   * @constructor
32918  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
32919  * The container MUST have some type of size defined for the grid to fill. The container will be
32920  * automatically set to position relative if it isn't already.
32921  * @param {Object} config A config object that sets properties on this grid.
32922  */
32923 Roo.grid.Grid = function(container, config){
32924         // initialize the container
32925         this.container = Roo.get(container);
32926         this.container.update("");
32927         this.container.setStyle("overflow", "hidden");
32928     this.container.addClass('x-grid-container');
32929
32930     this.id = this.container.id;
32931
32932     Roo.apply(this, config);
32933     // check and correct shorthanded configs
32934     if(this.ds){
32935         this.dataSource = this.ds;
32936         delete this.ds;
32937     }
32938     if(this.cm){
32939         this.colModel = this.cm;
32940         delete this.cm;
32941     }
32942     if(this.sm){
32943         this.selModel = this.sm;
32944         delete this.sm;
32945     }
32946
32947     if (this.selModel) {
32948         this.selModel = Roo.factory(this.selModel, Roo.grid);
32949         this.sm = this.selModel;
32950         this.sm.xmodule = this.xmodule || false;
32951     }
32952     if (typeof(this.colModel.config) == 'undefined') {
32953         this.colModel = new Roo.grid.ColumnModel(this.colModel);
32954         this.cm = this.colModel;
32955         this.cm.xmodule = this.xmodule || false;
32956     }
32957     if (this.dataSource) {
32958         this.dataSource= Roo.factory(this.dataSource, Roo.data);
32959         this.ds = this.dataSource;
32960         this.ds.xmodule = this.xmodule || false;
32961          
32962     }
32963     
32964     
32965     
32966     if(this.width){
32967         this.container.setWidth(this.width);
32968     }
32969
32970     if(this.height){
32971         this.container.setHeight(this.height);
32972     }
32973     /** @private */
32974         this.addEvents({
32975         // raw events
32976         /**
32977          * @event click
32978          * The raw click event for the entire grid.
32979          * @param {Roo.EventObject} e
32980          */
32981         "click" : true,
32982         /**
32983          * @event dblclick
32984          * The raw dblclick event for the entire grid.
32985          * @param {Roo.EventObject} e
32986          */
32987         "dblclick" : true,
32988         /**
32989          * @event contextmenu
32990          * The raw contextmenu event for the entire grid.
32991          * @param {Roo.EventObject} e
32992          */
32993         "contextmenu" : true,
32994         /**
32995          * @event mousedown
32996          * The raw mousedown event for the entire grid.
32997          * @param {Roo.EventObject} e
32998          */
32999         "mousedown" : true,
33000         /**
33001          * @event mouseup
33002          * The raw mouseup event for the entire grid.
33003          * @param {Roo.EventObject} e
33004          */
33005         "mouseup" : true,
33006         /**
33007          * @event mouseover
33008          * The raw mouseover event for the entire grid.
33009          * @param {Roo.EventObject} e
33010          */
33011         "mouseover" : true,
33012         /**
33013          * @event mouseout
33014          * The raw mouseout event for the entire grid.
33015          * @param {Roo.EventObject} e
33016          */
33017         "mouseout" : true,
33018         /**
33019          * @event keypress
33020          * The raw keypress event for the entire grid.
33021          * @param {Roo.EventObject} e
33022          */
33023         "keypress" : true,
33024         /**
33025          * @event keydown
33026          * The raw keydown event for the entire grid.
33027          * @param {Roo.EventObject} e
33028          */
33029         "keydown" : true,
33030
33031         // custom events
33032
33033         /**
33034          * @event cellclick
33035          * Fires when a cell is clicked
33036          * @param {Grid} this
33037          * @param {Number} rowIndex
33038          * @param {Number} columnIndex
33039          * @param {Roo.EventObject} e
33040          */
33041         "cellclick" : true,
33042         /**
33043          * @event celldblclick
33044          * Fires when a cell is double clicked
33045          * @param {Grid} this
33046          * @param {Number} rowIndex
33047          * @param {Number} columnIndex
33048          * @param {Roo.EventObject} e
33049          */
33050         "celldblclick" : true,
33051         /**
33052          * @event rowclick
33053          * Fires when a row is clicked
33054          * @param {Grid} this
33055          * @param {Number} rowIndex
33056          * @param {Roo.EventObject} e
33057          */
33058         "rowclick" : true,
33059         /**
33060          * @event rowdblclick
33061          * Fires when a row is double clicked
33062          * @param {Grid} this
33063          * @param {Number} rowIndex
33064          * @param {Roo.EventObject} e
33065          */
33066         "rowdblclick" : true,
33067         /**
33068          * @event headerclick
33069          * Fires when a header is clicked
33070          * @param {Grid} this
33071          * @param {Number} columnIndex
33072          * @param {Roo.EventObject} e
33073          */
33074         "headerclick" : true,
33075         /**
33076          * @event headerdblclick
33077          * Fires when a header cell is double clicked
33078          * @param {Grid} this
33079          * @param {Number} columnIndex
33080          * @param {Roo.EventObject} e
33081          */
33082         "headerdblclick" : true,
33083         /**
33084          * @event rowcontextmenu
33085          * Fires when a row is right clicked
33086          * @param {Grid} this
33087          * @param {Number} rowIndex
33088          * @param {Roo.EventObject} e
33089          */
33090         "rowcontextmenu" : true,
33091         /**
33092          * @event cellcontextmenu
33093          * Fires when a cell is right clicked
33094          * @param {Grid} this
33095          * @param {Number} rowIndex
33096          * @param {Number} cellIndex
33097          * @param {Roo.EventObject} e
33098          */
33099          "cellcontextmenu" : true,
33100         /**
33101          * @event headercontextmenu
33102          * Fires when a header is right clicked
33103          * @param {Grid} this
33104          * @param {Number} columnIndex
33105          * @param {Roo.EventObject} e
33106          */
33107         "headercontextmenu" : true,
33108         /**
33109          * @event bodyscroll
33110          * Fires when the body element is scrolled
33111          * @param {Number} scrollLeft
33112          * @param {Number} scrollTop
33113          */
33114         "bodyscroll" : true,
33115         /**
33116          * @event columnresize
33117          * Fires when the user resizes a column
33118          * @param {Number} columnIndex
33119          * @param {Number} newSize
33120          */
33121         "columnresize" : true,
33122         /**
33123          * @event columnmove
33124          * Fires when the user moves a column
33125          * @param {Number} oldIndex
33126          * @param {Number} newIndex
33127          */
33128         "columnmove" : true,
33129         /**
33130          * @event startdrag
33131          * Fires when row(s) start being dragged
33132          * @param {Grid} this
33133          * @param {Roo.GridDD} dd The drag drop object
33134          * @param {event} e The raw browser event
33135          */
33136         "startdrag" : true,
33137         /**
33138          * @event enddrag
33139          * Fires when a drag operation is complete
33140          * @param {Grid} this
33141          * @param {Roo.GridDD} dd The drag drop object
33142          * @param {event} e The raw browser event
33143          */
33144         "enddrag" : true,
33145         /**
33146          * @event dragdrop
33147          * Fires when dragged row(s) are dropped on a valid DD target
33148          * @param {Grid} this
33149          * @param {Roo.GridDD} dd The drag drop object
33150          * @param {String} targetId The target drag drop object
33151          * @param {event} e The raw browser event
33152          */
33153         "dragdrop" : true,
33154         /**
33155          * @event dragover
33156          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
33157          * @param {Grid} this
33158          * @param {Roo.GridDD} dd The drag drop object
33159          * @param {String} targetId The target drag drop object
33160          * @param {event} e The raw browser event
33161          */
33162         "dragover" : true,
33163         /**
33164          * @event dragenter
33165          *  Fires when the dragged row(s) first cross another DD target while being dragged
33166          * @param {Grid} this
33167          * @param {Roo.GridDD} dd The drag drop object
33168          * @param {String} targetId The target drag drop object
33169          * @param {event} e The raw browser event
33170          */
33171         "dragenter" : true,
33172         /**
33173          * @event dragout
33174          * Fires when the dragged row(s) leave another DD target while being dragged
33175          * @param {Grid} this
33176          * @param {Roo.GridDD} dd The drag drop object
33177          * @param {String} targetId The target drag drop object
33178          * @param {event} e The raw browser event
33179          */
33180         "dragout" : true,
33181         /**
33182          * @event rowclass
33183          * Fires when a row is rendered, so you can change add a style to it.
33184          * @param {GridView} gridview   The grid view
33185          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
33186          */
33187         'rowclass' : true,
33188
33189         /**
33190          * @event render
33191          * Fires when the grid is rendered
33192          * @param {Grid} grid
33193          */
33194         'render' : true
33195     });
33196
33197     Roo.grid.Grid.superclass.constructor.call(this);
33198 };
33199 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
33200     
33201     /**
33202      * @cfg {String} ddGroup - drag drop group.
33203      */
33204
33205     /**
33206      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
33207      */
33208     minColumnWidth : 25,
33209
33210     /**
33211      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
33212      * <b>on initial render.</b> It is more efficient to explicitly size the columns
33213      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
33214      */
33215     autoSizeColumns : false,
33216
33217     /**
33218      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
33219      */
33220     autoSizeHeaders : true,
33221
33222     /**
33223      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
33224      */
33225     monitorWindowResize : true,
33226
33227     /**
33228      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
33229      * rows measured to get a columns size. Default is 0 (all rows).
33230      */
33231     maxRowsToMeasure : 0,
33232
33233     /**
33234      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
33235      */
33236     trackMouseOver : true,
33237
33238     /**
33239     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
33240     */
33241     
33242     /**
33243     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
33244     */
33245     enableDragDrop : false,
33246     
33247     /**
33248     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
33249     */
33250     enableColumnMove : true,
33251     
33252     /**
33253     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
33254     */
33255     enableColumnHide : true,
33256     
33257     /**
33258     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
33259     */
33260     enableRowHeightSync : false,
33261     
33262     /**
33263     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
33264     */
33265     stripeRows : true,
33266     
33267     /**
33268     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
33269     */
33270     autoHeight : false,
33271
33272     /**
33273      * @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.
33274      */
33275     autoExpandColumn : false,
33276
33277     /**
33278     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
33279     * Default is 50.
33280     */
33281     autoExpandMin : 50,
33282
33283     /**
33284     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
33285     */
33286     autoExpandMax : 1000,
33287
33288     /**
33289     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
33290     */
33291     view : null,
33292
33293     /**
33294     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
33295     */
33296     loadMask : false,
33297     /**
33298     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
33299     */
33300     dropTarget: false,
33301     
33302    
33303     
33304     // private
33305     rendered : false,
33306
33307     /**
33308     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
33309     * of a fixed width. Default is false.
33310     */
33311     /**
33312     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
33313     */
33314     /**
33315      * Called once after all setup has been completed and the grid is ready to be rendered.
33316      * @return {Roo.grid.Grid} this
33317      */
33318     render : function()
33319     {
33320         var c = this.container;
33321         // try to detect autoHeight/width mode
33322         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
33323             this.autoHeight = true;
33324         }
33325         var view = this.getView();
33326         view.init(this);
33327
33328         c.on("click", this.onClick, this);
33329         c.on("dblclick", this.onDblClick, this);
33330         c.on("contextmenu", this.onContextMenu, this);
33331         c.on("keydown", this.onKeyDown, this);
33332
33333         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
33334
33335         this.getSelectionModel().init(this);
33336
33337         view.render();
33338
33339         if(this.loadMask){
33340             this.loadMask = new Roo.LoadMask(this.container,
33341                     Roo.apply({store:this.dataSource}, this.loadMask));
33342         }
33343         
33344         
33345         if (this.toolbar && this.toolbar.xtype) {
33346             this.toolbar.container = this.getView().getHeaderPanel(true);
33347             this.toolbar = new Roo.Toolbar(this.toolbar);
33348         }
33349         if (this.footer && this.footer.xtype) {
33350             this.footer.dataSource = this.getDataSource();
33351             this.footer.container = this.getView().getFooterPanel(true);
33352             this.footer = Roo.factory(this.footer, Roo);
33353         }
33354         if (this.dropTarget && this.dropTarget.xtype) {
33355             delete this.dropTarget.xtype;
33356             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
33357         }
33358         
33359         
33360         this.rendered = true;
33361         this.fireEvent('render', this);
33362         return this;
33363     },
33364
33365         /**
33366          * Reconfigures the grid to use a different Store and Column Model.
33367          * The View will be bound to the new objects and refreshed.
33368          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
33369          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
33370          */
33371     reconfigure : function(dataSource, colModel){
33372         if(this.loadMask){
33373             this.loadMask.destroy();
33374             this.loadMask = new Roo.LoadMask(this.container,
33375                     Roo.apply({store:dataSource}, this.loadMask));
33376         }
33377         this.view.bind(dataSource, colModel);
33378         this.dataSource = dataSource;
33379         this.colModel = colModel;
33380         this.view.refresh(true);
33381     },
33382
33383     // private
33384     onKeyDown : function(e){
33385         this.fireEvent("keydown", e);
33386     },
33387
33388     /**
33389      * Destroy this grid.
33390      * @param {Boolean} removeEl True to remove the element
33391      */
33392     destroy : function(removeEl, keepListeners){
33393         if(this.loadMask){
33394             this.loadMask.destroy();
33395         }
33396         var c = this.container;
33397         c.removeAllListeners();
33398         this.view.destroy();
33399         this.colModel.purgeListeners();
33400         if(!keepListeners){
33401             this.purgeListeners();
33402         }
33403         c.update("");
33404         if(removeEl === true){
33405             c.remove();
33406         }
33407     },
33408
33409     // private
33410     processEvent : function(name, e){
33411         this.fireEvent(name, e);
33412         var t = e.getTarget();
33413         var v = this.view;
33414         var header = v.findHeaderIndex(t);
33415         if(header !== false){
33416             this.fireEvent("header" + name, this, header, e);
33417         }else{
33418             var row = v.findRowIndex(t);
33419             var cell = v.findCellIndex(t);
33420             if(row !== false){
33421                 this.fireEvent("row" + name, this, row, e);
33422                 if(cell !== false){
33423                     this.fireEvent("cell" + name, this, row, cell, e);
33424                 }
33425             }
33426         }
33427     },
33428
33429     // private
33430     onClick : function(e){
33431         this.processEvent("click", e);
33432     },
33433
33434     // private
33435     onContextMenu : function(e, t){
33436         this.processEvent("contextmenu", e);
33437     },
33438
33439     // private
33440     onDblClick : function(e){
33441         this.processEvent("dblclick", e);
33442     },
33443
33444     // private
33445     walkCells : function(row, col, step, fn, scope){
33446         var cm = this.colModel, clen = cm.getColumnCount();
33447         var ds = this.dataSource, rlen = ds.getCount(), first = true;
33448         if(step < 0){
33449             if(col < 0){
33450                 row--;
33451                 first = false;
33452             }
33453             while(row >= 0){
33454                 if(!first){
33455                     col = clen-1;
33456                 }
33457                 first = false;
33458                 while(col >= 0){
33459                     if(fn.call(scope || this, row, col, cm) === true){
33460                         return [row, col];
33461                     }
33462                     col--;
33463                 }
33464                 row--;
33465             }
33466         } else {
33467             if(col >= clen){
33468                 row++;
33469                 first = false;
33470             }
33471             while(row < rlen){
33472                 if(!first){
33473                     col = 0;
33474                 }
33475                 first = false;
33476                 while(col < clen){
33477                     if(fn.call(scope || this, row, col, cm) === true){
33478                         return [row, col];
33479                     }
33480                     col++;
33481                 }
33482                 row++;
33483             }
33484         }
33485         return null;
33486     },
33487
33488     // private
33489     getSelections : function(){
33490         return this.selModel.getSelections();
33491     },
33492
33493     /**
33494      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
33495      * but if manual update is required this method will initiate it.
33496      */
33497     autoSize : function(){
33498         if(this.rendered){
33499             this.view.layout();
33500             if(this.view.adjustForScroll){
33501                 this.view.adjustForScroll();
33502             }
33503         }
33504     },
33505
33506     /**
33507      * Returns the grid's underlying element.
33508      * @return {Element} The element
33509      */
33510     getGridEl : function(){
33511         return this.container;
33512     },
33513
33514     // private for compatibility, overridden by editor grid
33515     stopEditing : function(){},
33516
33517     /**
33518      * Returns the grid's SelectionModel.
33519      * @return {SelectionModel}
33520      */
33521     getSelectionModel : function(){
33522         if(!this.selModel){
33523             this.selModel = new Roo.grid.RowSelectionModel();
33524         }
33525         return this.selModel;
33526     },
33527
33528     /**
33529      * Returns the grid's DataSource.
33530      * @return {DataSource}
33531      */
33532     getDataSource : function(){
33533         return this.dataSource;
33534     },
33535
33536     /**
33537      * Returns the grid's ColumnModel.
33538      * @return {ColumnModel}
33539      */
33540     getColumnModel : function(){
33541         return this.colModel;
33542     },
33543
33544     /**
33545      * Returns the grid's GridView object.
33546      * @return {GridView}
33547      */
33548     getView : function(){
33549         if(!this.view){
33550             this.view = new Roo.grid.GridView(this.viewConfig);
33551         }
33552         return this.view;
33553     },
33554     /**
33555      * Called to get grid's drag proxy text, by default returns this.ddText.
33556      * @return {String}
33557      */
33558     getDragDropText : function(){
33559         var count = this.selModel.getCount();
33560         return String.format(this.ddText, count, count == 1 ? '' : 's');
33561     }
33562 });
33563 /**
33564  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
33565  * %0 is replaced with the number of selected rows.
33566  * @type String
33567  */
33568 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
33569  * Based on:
33570  * Ext JS Library 1.1.1
33571  * Copyright(c) 2006-2007, Ext JS, LLC.
33572  *
33573  * Originally Released Under LGPL - original licence link has changed is not relivant.
33574  *
33575  * Fork - LGPL
33576  * <script type="text/javascript">
33577  */
33578  
33579 Roo.grid.AbstractGridView = function(){
33580         this.grid = null;
33581         
33582         this.events = {
33583             "beforerowremoved" : true,
33584             "beforerowsinserted" : true,
33585             "beforerefresh" : true,
33586             "rowremoved" : true,
33587             "rowsinserted" : true,
33588             "rowupdated" : true,
33589             "refresh" : true
33590         };
33591     Roo.grid.AbstractGridView.superclass.constructor.call(this);
33592 };
33593
33594 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
33595     rowClass : "x-grid-row",
33596     cellClass : "x-grid-cell",
33597     tdClass : "x-grid-td",
33598     hdClass : "x-grid-hd",
33599     splitClass : "x-grid-hd-split",
33600     
33601         init: function(grid){
33602         this.grid = grid;
33603                 var cid = this.grid.getGridEl().id;
33604         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
33605         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
33606         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
33607         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
33608         },
33609         
33610         getColumnRenderers : function(){
33611         var renderers = [];
33612         var cm = this.grid.colModel;
33613         var colCount = cm.getColumnCount();
33614         for(var i = 0; i < colCount; i++){
33615             renderers[i] = cm.getRenderer(i);
33616         }
33617         return renderers;
33618     },
33619     
33620     getColumnIds : function(){
33621         var ids = [];
33622         var cm = this.grid.colModel;
33623         var colCount = cm.getColumnCount();
33624         for(var i = 0; i < colCount; i++){
33625             ids[i] = cm.getColumnId(i);
33626         }
33627         return ids;
33628     },
33629     
33630     getDataIndexes : function(){
33631         if(!this.indexMap){
33632             this.indexMap = this.buildIndexMap();
33633         }
33634         return this.indexMap.colToData;
33635     },
33636     
33637     getColumnIndexByDataIndex : function(dataIndex){
33638         if(!this.indexMap){
33639             this.indexMap = this.buildIndexMap();
33640         }
33641         return this.indexMap.dataToCol[dataIndex];
33642     },
33643     
33644     /**
33645      * Set a css style for a column dynamically. 
33646      * @param {Number} colIndex The index of the column
33647      * @param {String} name The css property name
33648      * @param {String} value The css value
33649      */
33650     setCSSStyle : function(colIndex, name, value){
33651         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
33652         Roo.util.CSS.updateRule(selector, name, value);
33653     },
33654     
33655     generateRules : function(cm){
33656         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
33657         Roo.util.CSS.removeStyleSheet(rulesId);
33658         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33659             var cid = cm.getColumnId(i);
33660             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
33661                          this.tdSelector, cid, " {\n}\n",
33662                          this.hdSelector, cid, " {\n}\n",
33663                          this.splitSelector, cid, " {\n}\n");
33664         }
33665         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33666     }
33667 });/*
33668  * Based on:
33669  * Ext JS Library 1.1.1
33670  * Copyright(c) 2006-2007, Ext JS, LLC.
33671  *
33672  * Originally Released Under LGPL - original licence link has changed is not relivant.
33673  *
33674  * Fork - LGPL
33675  * <script type="text/javascript">
33676  */
33677
33678 // private
33679 // This is a support class used internally by the Grid components
33680 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
33681     this.grid = grid;
33682     this.view = grid.getView();
33683     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33684     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
33685     if(hd2){
33686         this.setHandleElId(Roo.id(hd));
33687         this.setOuterHandleElId(Roo.id(hd2));
33688     }
33689     this.scroll = false;
33690 };
33691 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
33692     maxDragWidth: 120,
33693     getDragData : function(e){
33694         var t = Roo.lib.Event.getTarget(e);
33695         var h = this.view.findHeaderCell(t);
33696         if(h){
33697             return {ddel: h.firstChild, header:h};
33698         }
33699         return false;
33700     },
33701
33702     onInitDrag : function(e){
33703         this.view.headersDisabled = true;
33704         var clone = this.dragData.ddel.cloneNode(true);
33705         clone.id = Roo.id();
33706         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
33707         this.proxy.update(clone);
33708         return true;
33709     },
33710
33711     afterValidDrop : function(){
33712         var v = this.view;
33713         setTimeout(function(){
33714             v.headersDisabled = false;
33715         }, 50);
33716     },
33717
33718     afterInvalidDrop : function(){
33719         var v = this.view;
33720         setTimeout(function(){
33721             v.headersDisabled = false;
33722         }, 50);
33723     }
33724 });
33725 /*
33726  * Based on:
33727  * Ext JS Library 1.1.1
33728  * Copyright(c) 2006-2007, Ext JS, LLC.
33729  *
33730  * Originally Released Under LGPL - original licence link has changed is not relivant.
33731  *
33732  * Fork - LGPL
33733  * <script type="text/javascript">
33734  */
33735 // private
33736 // This is a support class used internally by the Grid components
33737 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
33738     this.grid = grid;
33739     this.view = grid.getView();
33740     // split the proxies so they don't interfere with mouse events
33741     this.proxyTop = Roo.DomHelper.append(document.body, {
33742         cls:"col-move-top", html:"&#160;"
33743     }, true);
33744     this.proxyBottom = Roo.DomHelper.append(document.body, {
33745         cls:"col-move-bottom", html:"&#160;"
33746     }, true);
33747     this.proxyTop.hide = this.proxyBottom.hide = function(){
33748         this.setLeftTop(-100,-100);
33749         this.setStyle("visibility", "hidden");
33750     };
33751     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33752     // temporarily disabled
33753     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
33754     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
33755 };
33756 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
33757     proxyOffsets : [-4, -9],
33758     fly: Roo.Element.fly,
33759
33760     getTargetFromEvent : function(e){
33761         var t = Roo.lib.Event.getTarget(e);
33762         var cindex = this.view.findCellIndex(t);
33763         if(cindex !== false){
33764             return this.view.getHeaderCell(cindex);
33765         }
33766         return null;
33767     },
33768
33769     nextVisible : function(h){
33770         var v = this.view, cm = this.grid.colModel;
33771         h = h.nextSibling;
33772         while(h){
33773             if(!cm.isHidden(v.getCellIndex(h))){
33774                 return h;
33775             }
33776             h = h.nextSibling;
33777         }
33778         return null;
33779     },
33780
33781     prevVisible : function(h){
33782         var v = this.view, cm = this.grid.colModel;
33783         h = h.prevSibling;
33784         while(h){
33785             if(!cm.isHidden(v.getCellIndex(h))){
33786                 return h;
33787             }
33788             h = h.prevSibling;
33789         }
33790         return null;
33791     },
33792
33793     positionIndicator : function(h, n, e){
33794         var x = Roo.lib.Event.getPageX(e);
33795         var r = Roo.lib.Dom.getRegion(n.firstChild);
33796         var px, pt, py = r.top + this.proxyOffsets[1];
33797         if((r.right - x) <= (r.right-r.left)/2){
33798             px = r.right+this.view.borderWidth;
33799             pt = "after";
33800         }else{
33801             px = r.left;
33802             pt = "before";
33803         }
33804         var oldIndex = this.view.getCellIndex(h);
33805         var newIndex = this.view.getCellIndex(n);
33806
33807         if(this.grid.colModel.isFixed(newIndex)){
33808             return false;
33809         }
33810
33811         var locked = this.grid.colModel.isLocked(newIndex);
33812
33813         if(pt == "after"){
33814             newIndex++;
33815         }
33816         if(oldIndex < newIndex){
33817             newIndex--;
33818         }
33819         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
33820             return false;
33821         }
33822         px +=  this.proxyOffsets[0];
33823         this.proxyTop.setLeftTop(px, py);
33824         this.proxyTop.show();
33825         if(!this.bottomOffset){
33826             this.bottomOffset = this.view.mainHd.getHeight();
33827         }
33828         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
33829         this.proxyBottom.show();
33830         return pt;
33831     },
33832
33833     onNodeEnter : function(n, dd, e, data){
33834         if(data.header != n){
33835             this.positionIndicator(data.header, n, e);
33836         }
33837     },
33838
33839     onNodeOver : function(n, dd, e, data){
33840         var result = false;
33841         if(data.header != n){
33842             result = this.positionIndicator(data.header, n, e);
33843         }
33844         if(!result){
33845             this.proxyTop.hide();
33846             this.proxyBottom.hide();
33847         }
33848         return result ? this.dropAllowed : this.dropNotAllowed;
33849     },
33850
33851     onNodeOut : function(n, dd, e, data){
33852         this.proxyTop.hide();
33853         this.proxyBottom.hide();
33854     },
33855
33856     onNodeDrop : function(n, dd, e, data){
33857         var h = data.header;
33858         if(h != n){
33859             var cm = this.grid.colModel;
33860             var x = Roo.lib.Event.getPageX(e);
33861             var r = Roo.lib.Dom.getRegion(n.firstChild);
33862             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
33863             var oldIndex = this.view.getCellIndex(h);
33864             var newIndex = this.view.getCellIndex(n);
33865             var locked = cm.isLocked(newIndex);
33866             if(pt == "after"){
33867                 newIndex++;
33868             }
33869             if(oldIndex < newIndex){
33870                 newIndex--;
33871             }
33872             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
33873                 return false;
33874             }
33875             cm.setLocked(oldIndex, locked, true);
33876             cm.moveColumn(oldIndex, newIndex);
33877             this.grid.fireEvent("columnmove", oldIndex, newIndex);
33878             return true;
33879         }
33880         return false;
33881     }
33882 });
33883 /*
33884  * Based on:
33885  * Ext JS Library 1.1.1
33886  * Copyright(c) 2006-2007, Ext JS, LLC.
33887  *
33888  * Originally Released Under LGPL - original licence link has changed is not relivant.
33889  *
33890  * Fork - LGPL
33891  * <script type="text/javascript">
33892  */
33893   
33894 /**
33895  * @class Roo.grid.GridView
33896  * @extends Roo.util.Observable
33897  *
33898  * @constructor
33899  * @param {Object} config
33900  */
33901 Roo.grid.GridView = function(config){
33902     Roo.grid.GridView.superclass.constructor.call(this);
33903     this.el = null;
33904
33905     Roo.apply(this, config);
33906 };
33907
33908 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
33909
33910     /**
33911      * Override this function to apply custom css classes to rows during rendering
33912      * @param {Record} record The record
33913      * @param {Number} index
33914      * @method getRowClass
33915      */
33916     rowClass : "x-grid-row",
33917
33918     cellClass : "x-grid-col",
33919
33920     tdClass : "x-grid-td",
33921
33922     hdClass : "x-grid-hd",
33923
33924     splitClass : "x-grid-split",
33925
33926     sortClasses : ["sort-asc", "sort-desc"],
33927
33928     enableMoveAnim : false,
33929
33930     hlColor: "C3DAF9",
33931
33932     dh : Roo.DomHelper,
33933
33934     fly : Roo.Element.fly,
33935
33936     css : Roo.util.CSS,
33937
33938     borderWidth: 1,
33939
33940     splitOffset: 3,
33941
33942     scrollIncrement : 22,
33943
33944     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
33945
33946     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
33947
33948     bind : function(ds, cm){
33949         if(this.ds){
33950             this.ds.un("load", this.onLoad, this);
33951             this.ds.un("datachanged", this.onDataChange, this);
33952             this.ds.un("add", this.onAdd, this);
33953             this.ds.un("remove", this.onRemove, this);
33954             this.ds.un("update", this.onUpdate, this);
33955             this.ds.un("clear", this.onClear, this);
33956         }
33957         if(ds){
33958             ds.on("load", this.onLoad, this);
33959             ds.on("datachanged", this.onDataChange, this);
33960             ds.on("add", this.onAdd, this);
33961             ds.on("remove", this.onRemove, this);
33962             ds.on("update", this.onUpdate, this);
33963             ds.on("clear", this.onClear, this);
33964         }
33965         this.ds = ds;
33966
33967         if(this.cm){
33968             this.cm.un("widthchange", this.onColWidthChange, this);
33969             this.cm.un("headerchange", this.onHeaderChange, this);
33970             this.cm.un("hiddenchange", this.onHiddenChange, this);
33971             this.cm.un("columnmoved", this.onColumnMove, this);
33972             this.cm.un("columnlockchange", this.onColumnLock, this);
33973         }
33974         if(cm){
33975             this.generateRules(cm);
33976             cm.on("widthchange", this.onColWidthChange, this);
33977             cm.on("headerchange", this.onHeaderChange, this);
33978             cm.on("hiddenchange", this.onHiddenChange, this);
33979             cm.on("columnmoved", this.onColumnMove, this);
33980             cm.on("columnlockchange", this.onColumnLock, this);
33981         }
33982         this.cm = cm;
33983     },
33984
33985     init: function(grid){
33986         Roo.grid.GridView.superclass.init.call(this, grid);
33987
33988         this.bind(grid.dataSource, grid.colModel);
33989
33990         grid.on("headerclick", this.handleHeaderClick, this);
33991
33992         if(grid.trackMouseOver){
33993             grid.on("mouseover", this.onRowOver, this);
33994             grid.on("mouseout", this.onRowOut, this);
33995         }
33996         grid.cancelTextSelection = function(){};
33997         this.gridId = grid.id;
33998
33999         var tpls = this.templates || {};
34000
34001         if(!tpls.master){
34002             tpls.master = new Roo.Template(
34003                '<div class="x-grid" hidefocus="true">',
34004                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
34005                   '<div class="x-grid-topbar"></div>',
34006                   '<div class="x-grid-scroller"><div></div></div>',
34007                   '<div class="x-grid-locked">',
34008                       '<div class="x-grid-header">{lockedHeader}</div>',
34009                       '<div class="x-grid-body">{lockedBody}</div>',
34010                   "</div>",
34011                   '<div class="x-grid-viewport">',
34012                       '<div class="x-grid-header">{header}</div>',
34013                       '<div class="x-grid-body">{body}</div>',
34014                   "</div>",
34015                   '<div class="x-grid-bottombar"></div>',
34016                  
34017                   '<div class="x-grid-resize-proxy">&#160;</div>',
34018                "</div>"
34019             );
34020             tpls.master.disableformats = true;
34021         }
34022
34023         if(!tpls.header){
34024             tpls.header = new Roo.Template(
34025                '<table border="0" cellspacing="0" cellpadding="0">',
34026                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
34027                "</table>{splits}"
34028             );
34029             tpls.header.disableformats = true;
34030         }
34031         tpls.header.compile();
34032
34033         if(!tpls.hcell){
34034             tpls.hcell = new Roo.Template(
34035                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
34036                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
34037                 "</div></td>"
34038              );
34039              tpls.hcell.disableFormats = true;
34040         }
34041         tpls.hcell.compile();
34042
34043         if(!tpls.hsplit){
34044             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
34045             tpls.hsplit.disableFormats = true;
34046         }
34047         tpls.hsplit.compile();
34048
34049         if(!tpls.body){
34050             tpls.body = new Roo.Template(
34051                '<table border="0" cellspacing="0" cellpadding="0">',
34052                "<tbody>{rows}</tbody>",
34053                "</table>"
34054             );
34055             tpls.body.disableFormats = true;
34056         }
34057         tpls.body.compile();
34058
34059         if(!tpls.row){
34060             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
34061             tpls.row.disableFormats = true;
34062         }
34063         tpls.row.compile();
34064
34065         if(!tpls.cell){
34066             tpls.cell = new Roo.Template(
34067                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
34068                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
34069                 "</td>"
34070             );
34071             tpls.cell.disableFormats = true;
34072         }
34073         tpls.cell.compile();
34074
34075         this.templates = tpls;
34076     },
34077
34078     // remap these for backwards compat
34079     onColWidthChange : function(){
34080         this.updateColumns.apply(this, arguments);
34081     },
34082     onHeaderChange : function(){
34083         this.updateHeaders.apply(this, arguments);
34084     }, 
34085     onHiddenChange : function(){
34086         this.handleHiddenChange.apply(this, arguments);
34087     },
34088     onColumnMove : function(){
34089         this.handleColumnMove.apply(this, arguments);
34090     },
34091     onColumnLock : function(){
34092         this.handleLockChange.apply(this, arguments);
34093     },
34094
34095     onDataChange : function(){
34096         this.refresh();
34097         this.updateHeaderSortState();
34098     },
34099
34100     onClear : function(){
34101         this.refresh();
34102     },
34103
34104     onUpdate : function(ds, record){
34105         this.refreshRow(record);
34106     },
34107
34108     refreshRow : function(record){
34109         var ds = this.ds, index;
34110         if(typeof record == 'number'){
34111             index = record;
34112             record = ds.getAt(index);
34113         }else{
34114             index = ds.indexOf(record);
34115         }
34116         this.insertRows(ds, index, index, true);
34117         this.onRemove(ds, record, index+1, true);
34118         this.syncRowHeights(index, index);
34119         this.layout();
34120         this.fireEvent("rowupdated", this, index, record);
34121     },
34122
34123     onAdd : function(ds, records, index){
34124         this.insertRows(ds, index, index + (records.length-1));
34125     },
34126
34127     onRemove : function(ds, record, index, isUpdate){
34128         if(isUpdate !== true){
34129             this.fireEvent("beforerowremoved", this, index, record);
34130         }
34131         var bt = this.getBodyTable(), lt = this.getLockedTable();
34132         if(bt.rows[index]){
34133             bt.firstChild.removeChild(bt.rows[index]);
34134         }
34135         if(lt.rows[index]){
34136             lt.firstChild.removeChild(lt.rows[index]);
34137         }
34138         if(isUpdate !== true){
34139             this.stripeRows(index);
34140             this.syncRowHeights(index, index);
34141             this.layout();
34142             this.fireEvent("rowremoved", this, index, record);
34143         }
34144     },
34145
34146     onLoad : function(){
34147         this.scrollToTop();
34148     },
34149
34150     /**
34151      * Scrolls the grid to the top
34152      */
34153     scrollToTop : function(){
34154         if(this.scroller){
34155             this.scroller.dom.scrollTop = 0;
34156             this.syncScroll();
34157         }
34158     },
34159
34160     /**
34161      * Gets a panel in the header of the grid that can be used for toolbars etc.
34162      * After modifying the contents of this panel a call to grid.autoSize() may be
34163      * required to register any changes in size.
34164      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
34165      * @return Roo.Element
34166      */
34167     getHeaderPanel : function(doShow){
34168         if(doShow){
34169             this.headerPanel.show();
34170         }
34171         return this.headerPanel;
34172     },
34173
34174     /**
34175      * Gets a panel in the footer of the grid that can be used for toolbars etc.
34176      * After modifying the contents of this panel a call to grid.autoSize() may be
34177      * required to register any changes in size.
34178      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
34179      * @return Roo.Element
34180      */
34181     getFooterPanel : function(doShow){
34182         if(doShow){
34183             this.footerPanel.show();
34184         }
34185         return this.footerPanel;
34186     },
34187
34188     initElements : function(){
34189         var E = Roo.Element;
34190         var el = this.grid.getGridEl().dom.firstChild;
34191         var cs = el.childNodes;
34192
34193         this.el = new E(el);
34194         
34195          this.focusEl = new E(el.firstChild);
34196         this.focusEl.swallowEvent("click", true);
34197         
34198         this.headerPanel = new E(cs[1]);
34199         this.headerPanel.enableDisplayMode("block");
34200
34201         this.scroller = new E(cs[2]);
34202         this.scrollSizer = new E(this.scroller.dom.firstChild);
34203
34204         this.lockedWrap = new E(cs[3]);
34205         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
34206         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
34207
34208         this.mainWrap = new E(cs[4]);
34209         this.mainHd = new E(this.mainWrap.dom.firstChild);
34210         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
34211
34212         this.footerPanel = new E(cs[5]);
34213         this.footerPanel.enableDisplayMode("block");
34214
34215         this.resizeProxy = new E(cs[6]);
34216
34217         this.headerSelector = String.format(
34218            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
34219            this.lockedHd.id, this.mainHd.id
34220         );
34221
34222         this.splitterSelector = String.format(
34223            '#{0} div.x-grid-split, #{1} div.x-grid-split',
34224            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
34225         );
34226     },
34227     idToCssName : function(s)
34228     {
34229         return s.replace(/[^a-z0-9]+/ig, '-');
34230     },
34231
34232     getHeaderCell : function(index){
34233         return Roo.DomQuery.select(this.headerSelector)[index];
34234     },
34235
34236     getHeaderCellMeasure : function(index){
34237         return this.getHeaderCell(index).firstChild;
34238     },
34239
34240     getHeaderCellText : function(index){
34241         return this.getHeaderCell(index).firstChild.firstChild;
34242     },
34243
34244     getLockedTable : function(){
34245         return this.lockedBody.dom.firstChild;
34246     },
34247
34248     getBodyTable : function(){
34249         return this.mainBody.dom.firstChild;
34250     },
34251
34252     getLockedRow : function(index){
34253         return this.getLockedTable().rows[index];
34254     },
34255
34256     getRow : function(index){
34257         return this.getBodyTable().rows[index];
34258     },
34259
34260     getRowComposite : function(index){
34261         if(!this.rowEl){
34262             this.rowEl = new Roo.CompositeElementLite();
34263         }
34264         var els = [], lrow, mrow;
34265         if(lrow = this.getLockedRow(index)){
34266             els.push(lrow);
34267         }
34268         if(mrow = this.getRow(index)){
34269             els.push(mrow);
34270         }
34271         this.rowEl.elements = els;
34272         return this.rowEl;
34273     },
34274     /**
34275      * Gets the 'td' of the cell
34276      * 
34277      * @param {Integer} rowIndex row to select
34278      * @param {Integer} colIndex column to select
34279      * 
34280      * @return {Object} 
34281      */
34282     getCell : function(rowIndex, colIndex){
34283         var locked = this.cm.getLockedCount();
34284         var source;
34285         if(colIndex < locked){
34286             source = this.lockedBody.dom.firstChild;
34287         }else{
34288             source = this.mainBody.dom.firstChild;
34289             colIndex -= locked;
34290         }
34291         return source.rows[rowIndex].childNodes[colIndex];
34292     },
34293
34294     getCellText : function(rowIndex, colIndex){
34295         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
34296     },
34297
34298     getCellBox : function(cell){
34299         var b = this.fly(cell).getBox();
34300         if(Roo.isOpera){ // opera fails to report the Y
34301             b.y = cell.offsetTop + this.mainBody.getY();
34302         }
34303         return b;
34304     },
34305
34306     getCellIndex : function(cell){
34307         var id = String(cell.className).match(this.cellRE);
34308         if(id){
34309             return parseInt(id[1], 10);
34310         }
34311         return 0;
34312     },
34313
34314     findHeaderIndex : function(n){
34315         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
34316         return r ? this.getCellIndex(r) : false;
34317     },
34318
34319     findHeaderCell : function(n){
34320         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
34321         return r ? r : false;
34322     },
34323
34324     findRowIndex : function(n){
34325         if(!n){
34326             return false;
34327         }
34328         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
34329         return r ? r.rowIndex : false;
34330     },
34331
34332     findCellIndex : function(node){
34333         var stop = this.el.dom;
34334         while(node && node != stop){
34335             if(this.findRE.test(node.className)){
34336                 return this.getCellIndex(node);
34337             }
34338             node = node.parentNode;
34339         }
34340         return false;
34341     },
34342
34343     getColumnId : function(index){
34344         return this.cm.getColumnId(index);
34345     },
34346
34347     getSplitters : function()
34348     {
34349         if(this.splitterSelector){
34350            return Roo.DomQuery.select(this.splitterSelector);
34351         }else{
34352             return null;
34353       }
34354     },
34355
34356     getSplitter : function(index){
34357         return this.getSplitters()[index];
34358     },
34359
34360     onRowOver : function(e, t){
34361         var row;
34362         if((row = this.findRowIndex(t)) !== false){
34363             this.getRowComposite(row).addClass("x-grid-row-over");
34364         }
34365     },
34366
34367     onRowOut : function(e, t){
34368         var row;
34369         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
34370             this.getRowComposite(row).removeClass("x-grid-row-over");
34371         }
34372     },
34373
34374     renderHeaders : function(){
34375         var cm = this.cm;
34376         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
34377         var cb = [], lb = [], sb = [], lsb = [], p = {};
34378         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34379             p.cellId = "x-grid-hd-0-" + i;
34380             p.splitId = "x-grid-csplit-0-" + i;
34381             p.id = cm.getColumnId(i);
34382             p.title = cm.getColumnTooltip(i) || "";
34383             p.value = cm.getColumnHeader(i) || "";
34384             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
34385             if(!cm.isLocked(i)){
34386                 cb[cb.length] = ct.apply(p);
34387                 sb[sb.length] = st.apply(p);
34388             }else{
34389                 lb[lb.length] = ct.apply(p);
34390                 lsb[lsb.length] = st.apply(p);
34391             }
34392         }
34393         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
34394                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
34395     },
34396
34397     updateHeaders : function(){
34398         var html = this.renderHeaders();
34399         this.lockedHd.update(html[0]);
34400         this.mainHd.update(html[1]);
34401     },
34402
34403     /**
34404      * Focuses the specified row.
34405      * @param {Number} row The row index
34406      */
34407     focusRow : function(row)
34408     {
34409         //Roo.log('GridView.focusRow');
34410         var x = this.scroller.dom.scrollLeft;
34411         this.focusCell(row, 0, false);
34412         this.scroller.dom.scrollLeft = x;
34413     },
34414
34415     /**
34416      * Focuses the specified cell.
34417      * @param {Number} row The row index
34418      * @param {Number} col The column index
34419      * @param {Boolean} hscroll false to disable horizontal scrolling
34420      */
34421     focusCell : function(row, col, hscroll)
34422     {
34423         //Roo.log('GridView.focusCell');
34424         var el = this.ensureVisible(row, col, hscroll);
34425         this.focusEl.alignTo(el, "tl-tl");
34426         if(Roo.isGecko){
34427             this.focusEl.focus();
34428         }else{
34429             this.focusEl.focus.defer(1, this.focusEl);
34430         }
34431     },
34432
34433     /**
34434      * Scrolls the specified cell into view
34435      * @param {Number} row The row index
34436      * @param {Number} col The column index
34437      * @param {Boolean} hscroll false to disable horizontal scrolling
34438      */
34439     ensureVisible : function(row, col, hscroll)
34440     {
34441         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
34442         //return null; //disable for testing.
34443         if(typeof row != "number"){
34444             row = row.rowIndex;
34445         }
34446         if(row < 0 && row >= this.ds.getCount()){
34447             return  null;
34448         }
34449         col = (col !== undefined ? col : 0);
34450         var cm = this.grid.colModel;
34451         while(cm.isHidden(col)){
34452             col++;
34453         }
34454
34455         var el = this.getCell(row, col);
34456         if(!el){
34457             return null;
34458         }
34459         var c = this.scroller.dom;
34460
34461         var ctop = parseInt(el.offsetTop, 10);
34462         var cleft = parseInt(el.offsetLeft, 10);
34463         var cbot = ctop + el.offsetHeight;
34464         var cright = cleft + el.offsetWidth;
34465         
34466         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
34467         var stop = parseInt(c.scrollTop, 10);
34468         var sleft = parseInt(c.scrollLeft, 10);
34469         var sbot = stop + ch;
34470         var sright = sleft + c.clientWidth;
34471         /*
34472         Roo.log('GridView.ensureVisible:' +
34473                 ' ctop:' + ctop +
34474                 ' c.clientHeight:' + c.clientHeight +
34475                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
34476                 ' stop:' + stop +
34477                 ' cbot:' + cbot +
34478                 ' sbot:' + sbot +
34479                 ' ch:' + ch  
34480                 );
34481         */
34482         if(ctop < stop){
34483              c.scrollTop = ctop;
34484             //Roo.log("set scrolltop to ctop DISABLE?");
34485         }else if(cbot > sbot){
34486             //Roo.log("set scrolltop to cbot-ch");
34487             c.scrollTop = cbot-ch;
34488         }
34489         
34490         if(hscroll !== false){
34491             if(cleft < sleft){
34492                 c.scrollLeft = cleft;
34493             }else if(cright > sright){
34494                 c.scrollLeft = cright-c.clientWidth;
34495             }
34496         }
34497          
34498         return el;
34499     },
34500
34501     updateColumns : function(){
34502         this.grid.stopEditing();
34503         var cm = this.grid.colModel, colIds = this.getColumnIds();
34504         //var totalWidth = cm.getTotalWidth();
34505         var pos = 0;
34506         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34507             //if(cm.isHidden(i)) continue;
34508             var w = cm.getColumnWidth(i);
34509             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
34510             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
34511         }
34512         this.updateSplitters();
34513     },
34514
34515     generateRules : function(cm){
34516         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
34517         Roo.util.CSS.removeStyleSheet(rulesId);
34518         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34519             var cid = cm.getColumnId(i);
34520             var align = '';
34521             if(cm.config[i].align){
34522                 align = 'text-align:'+cm.config[i].align+';';
34523             }
34524             var hidden = '';
34525             if(cm.isHidden(i)){
34526                 hidden = 'display:none;';
34527             }
34528             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
34529             ruleBuf.push(
34530                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
34531                     this.hdSelector, cid, " {\n", align, width, "}\n",
34532                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
34533                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
34534         }
34535         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
34536     },
34537
34538     updateSplitters : function(){
34539         var cm = this.cm, s = this.getSplitters();
34540         if(s){ // splitters not created yet
34541             var pos = 0, locked = true;
34542             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34543                 if(cm.isHidden(i)) continue;
34544                 var w = cm.getColumnWidth(i); // make sure it's a number
34545                 if(!cm.isLocked(i) && locked){
34546                     pos = 0;
34547                     locked = false;
34548                 }
34549                 pos += w;
34550                 s[i].style.left = (pos-this.splitOffset) + "px";
34551             }
34552         }
34553     },
34554
34555     handleHiddenChange : function(colModel, colIndex, hidden){
34556         if(hidden){
34557             this.hideColumn(colIndex);
34558         }else{
34559             this.unhideColumn(colIndex);
34560         }
34561     },
34562
34563     hideColumn : function(colIndex){
34564         var cid = this.getColumnId(colIndex);
34565         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
34566         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
34567         if(Roo.isSafari){
34568             this.updateHeaders();
34569         }
34570         this.updateSplitters();
34571         this.layout();
34572     },
34573
34574     unhideColumn : function(colIndex){
34575         var cid = this.getColumnId(colIndex);
34576         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
34577         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
34578
34579         if(Roo.isSafari){
34580             this.updateHeaders();
34581         }
34582         this.updateSplitters();
34583         this.layout();
34584     },
34585
34586     insertRows : function(dm, firstRow, lastRow, isUpdate){
34587         if(firstRow == 0 && lastRow == dm.getCount()-1){
34588             this.refresh();
34589         }else{
34590             if(!isUpdate){
34591                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
34592             }
34593             var s = this.getScrollState();
34594             var markup = this.renderRows(firstRow, lastRow);
34595             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
34596             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
34597             this.restoreScroll(s);
34598             if(!isUpdate){
34599                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
34600                 this.syncRowHeights(firstRow, lastRow);
34601                 this.stripeRows(firstRow);
34602                 this.layout();
34603             }
34604         }
34605     },
34606
34607     bufferRows : function(markup, target, index){
34608         var before = null, trows = target.rows, tbody = target.tBodies[0];
34609         if(index < trows.length){
34610             before = trows[index];
34611         }
34612         var b = document.createElement("div");
34613         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
34614         var rows = b.firstChild.rows;
34615         for(var i = 0, len = rows.length; i < len; i++){
34616             if(before){
34617                 tbody.insertBefore(rows[0], before);
34618             }else{
34619                 tbody.appendChild(rows[0]);
34620             }
34621         }
34622         b.innerHTML = "";
34623         b = null;
34624     },
34625
34626     deleteRows : function(dm, firstRow, lastRow){
34627         if(dm.getRowCount()<1){
34628             this.fireEvent("beforerefresh", this);
34629             this.mainBody.update("");
34630             this.lockedBody.update("");
34631             this.fireEvent("refresh", this);
34632         }else{
34633             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
34634             var bt = this.getBodyTable();
34635             var tbody = bt.firstChild;
34636             var rows = bt.rows;
34637             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
34638                 tbody.removeChild(rows[firstRow]);
34639             }
34640             this.stripeRows(firstRow);
34641             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
34642         }
34643     },
34644
34645     updateRows : function(dataSource, firstRow, lastRow){
34646         var s = this.getScrollState();
34647         this.refresh();
34648         this.restoreScroll(s);
34649     },
34650
34651     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
34652         if(!noRefresh){
34653            this.refresh();
34654         }
34655         this.updateHeaderSortState();
34656     },
34657
34658     getScrollState : function(){
34659         
34660         var sb = this.scroller.dom;
34661         return {left: sb.scrollLeft, top: sb.scrollTop};
34662     },
34663
34664     stripeRows : function(startRow){
34665         if(!this.grid.stripeRows || this.ds.getCount() < 1){
34666             return;
34667         }
34668         startRow = startRow || 0;
34669         var rows = this.getBodyTable().rows;
34670         var lrows = this.getLockedTable().rows;
34671         var cls = ' x-grid-row-alt ';
34672         for(var i = startRow, len = rows.length; i < len; i++){
34673             var row = rows[i], lrow = lrows[i];
34674             var isAlt = ((i+1) % 2 == 0);
34675             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
34676             if(isAlt == hasAlt){
34677                 continue;
34678             }
34679             if(isAlt){
34680                 row.className += " x-grid-row-alt";
34681             }else{
34682                 row.className = row.className.replace("x-grid-row-alt", "");
34683             }
34684             if(lrow){
34685                 lrow.className = row.className;
34686             }
34687         }
34688     },
34689
34690     restoreScroll : function(state){
34691         //Roo.log('GridView.restoreScroll');
34692         var sb = this.scroller.dom;
34693         sb.scrollLeft = state.left;
34694         sb.scrollTop = state.top;
34695         this.syncScroll();
34696     },
34697
34698     syncScroll : function(){
34699         //Roo.log('GridView.syncScroll');
34700         var sb = this.scroller.dom;
34701         var sh = this.mainHd.dom;
34702         var bs = this.mainBody.dom;
34703         var lv = this.lockedBody.dom;
34704         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
34705         lv.scrollTop = bs.scrollTop = sb.scrollTop;
34706     },
34707
34708     handleScroll : function(e){
34709         this.syncScroll();
34710         var sb = this.scroller.dom;
34711         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
34712         e.stopEvent();
34713     },
34714
34715     handleWheel : function(e){
34716         var d = e.getWheelDelta();
34717         this.scroller.dom.scrollTop -= d*22;
34718         // set this here to prevent jumpy scrolling on large tables
34719         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
34720         e.stopEvent();
34721     },
34722
34723     renderRows : function(startRow, endRow){
34724         // pull in all the crap needed to render rows
34725         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
34726         var colCount = cm.getColumnCount();
34727
34728         if(ds.getCount() < 1){
34729             return ["", ""];
34730         }
34731
34732         // build a map for all the columns
34733         var cs = [];
34734         for(var i = 0; i < colCount; i++){
34735             var name = cm.getDataIndex(i);
34736             cs[i] = {
34737                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
34738                 renderer : cm.getRenderer(i),
34739                 id : cm.getColumnId(i),
34740                 locked : cm.isLocked(i)
34741             };
34742         }
34743
34744         startRow = startRow || 0;
34745         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
34746
34747         // records to render
34748         var rs = ds.getRange(startRow, endRow);
34749
34750         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
34751     },
34752
34753     // As much as I hate to duplicate code, this was branched because FireFox really hates
34754     // [].join("") on strings. The performance difference was substantial enough to
34755     // branch this function
34756     doRender : Roo.isGecko ?
34757             function(cs, rs, ds, startRow, colCount, stripe){
34758                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34759                 // buffers
34760                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34761                 
34762                 var hasListener = this.grid.hasListener('rowclass');
34763                 var rowcfg = {};
34764                 for(var j = 0, len = rs.length; j < len; j++){
34765                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
34766                     for(var i = 0; i < colCount; i++){
34767                         c = cs[i];
34768                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34769                         p.id = c.id;
34770                         p.css = p.attr = "";
34771                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34772                         if(p.value == undefined || p.value === "") p.value = "&#160;";
34773                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
34774                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
34775                         }
34776                         var markup = ct.apply(p);
34777                         if(!c.locked){
34778                             cb+= markup;
34779                         }else{
34780                             lcb+= markup;
34781                         }
34782                     }
34783                     var alt = [];
34784                     if(stripe && ((rowIndex+1) % 2 == 0)){
34785                         alt.push("x-grid-row-alt")
34786                     }
34787                     if(r.dirty){
34788                         alt.push(  " x-grid-dirty-row");
34789                     }
34790                     rp.cells = lcb;
34791                     if(this.getRowClass){
34792                         alt.push(this.getRowClass(r, rowIndex));
34793                     }
34794                     if (hasListener) {
34795                         rowcfg = {
34796                              
34797                             record: r,
34798                             rowIndex : rowIndex,
34799                             rowClass : ''
34800                         }
34801                         this.grid.fireEvent('rowclass', this, rowcfg);
34802                         alt.push(rowcfg.rowClass);
34803                     }
34804                     rp.alt = alt.join(" ");
34805                     lbuf+= rt.apply(rp);
34806                     rp.cells = cb;
34807                     buf+=  rt.apply(rp);
34808                 }
34809                 return [lbuf, buf];
34810             } :
34811             function(cs, rs, ds, startRow, colCount, stripe){
34812                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34813                 // buffers
34814                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34815                 var hasListener = this.grid.hasListener('rowclass');
34816                 var rowcfg = {};
34817                 for(var j = 0, len = rs.length; j < len; j++){
34818                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
34819                     for(var i = 0; i < colCount; i++){
34820                         c = cs[i];
34821                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34822                         p.id = c.id;
34823                         p.css = p.attr = "";
34824                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34825                         if(p.value == undefined || p.value === "") p.value = "&#160;";
34826                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
34827                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
34828                         }
34829                         var markup = ct.apply(p);
34830                         if(!c.locked){
34831                             cb[cb.length] = markup;
34832                         }else{
34833                             lcb[lcb.length] = markup;
34834                         }
34835                     }
34836                     var alt = [];
34837                     if(stripe && ((rowIndex+1) % 2 == 0)){
34838                         alt.push( "x-grid-row-alt");
34839                     }
34840                     if(r.dirty){
34841                         alt.push(" x-grid-dirty-row");
34842                     }
34843                     rp.cells = lcb;
34844                     if(this.getRowClass){
34845                         alt.push( this.getRowClass(r, rowIndex));
34846                     }
34847                     if (hasListener) {
34848                         rowcfg = {
34849                              
34850                             record: r,
34851                             rowIndex : rowIndex,
34852                             rowClass : ''
34853                         }
34854                         this.grid.fireEvent('rowclass', this, rowcfg);
34855                         alt.push(rowcfg.rowClass);
34856                     }
34857                     rp.alt = alt.join(" ");
34858                     rp.cells = lcb.join("");
34859                     lbuf[lbuf.length] = rt.apply(rp);
34860                     rp.cells = cb.join("");
34861                     buf[buf.length] =  rt.apply(rp);
34862                 }
34863                 return [lbuf.join(""), buf.join("")];
34864             },
34865
34866     renderBody : function(){
34867         var markup = this.renderRows();
34868         var bt = this.templates.body;
34869         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
34870     },
34871
34872     /**
34873      * Refreshes the grid
34874      * @param {Boolean} headersToo
34875      */
34876     refresh : function(headersToo){
34877         this.fireEvent("beforerefresh", this);
34878         this.grid.stopEditing();
34879         var result = this.renderBody();
34880         this.lockedBody.update(result[0]);
34881         this.mainBody.update(result[1]);
34882         if(headersToo === true){
34883             this.updateHeaders();
34884             this.updateColumns();
34885             this.updateSplitters();
34886             this.updateHeaderSortState();
34887         }
34888         this.syncRowHeights();
34889         this.layout();
34890         this.fireEvent("refresh", this);
34891     },
34892
34893     handleColumnMove : function(cm, oldIndex, newIndex){
34894         this.indexMap = null;
34895         var s = this.getScrollState();
34896         this.refresh(true);
34897         this.restoreScroll(s);
34898         this.afterMove(newIndex);
34899     },
34900
34901     afterMove : function(colIndex){
34902         if(this.enableMoveAnim && Roo.enableFx){
34903             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
34904         }
34905         // if multisort - fix sortOrder, and reload..
34906         if (this.grid.dataSource.multiSort) {
34907             // the we can call sort again..
34908             var dm = this.grid.dataSource;
34909             var cm = this.grid.colModel;
34910             var so = [];
34911             for(var i = 0; i < cm.config.length; i++ ) {
34912                 
34913                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
34914                     continue; // dont' bother, it's not in sort list or being set.
34915                 }
34916                 
34917                 so.push(cm.config[i].dataIndex);
34918             };
34919             dm.sortOrder = so;
34920             dm.load(dm.lastOptions);
34921             
34922             
34923         }
34924         
34925     },
34926
34927     updateCell : function(dm, rowIndex, dataIndex){
34928         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
34929         if(typeof colIndex == "undefined"){ // not present in grid
34930             return;
34931         }
34932         var cm = this.grid.colModel;
34933         var cell = this.getCell(rowIndex, colIndex);
34934         var cellText = this.getCellText(rowIndex, colIndex);
34935
34936         var p = {
34937             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
34938             id : cm.getColumnId(colIndex),
34939             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
34940         };
34941         var renderer = cm.getRenderer(colIndex);
34942         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
34943         if(typeof val == "undefined" || val === "") val = "&#160;";
34944         cellText.innerHTML = val;
34945         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
34946         this.syncRowHeights(rowIndex, rowIndex);
34947     },
34948
34949     calcColumnWidth : function(colIndex, maxRowsToMeasure){
34950         var maxWidth = 0;
34951         if(this.grid.autoSizeHeaders){
34952             var h = this.getHeaderCellMeasure(colIndex);
34953             maxWidth = Math.max(maxWidth, h.scrollWidth);
34954         }
34955         var tb, index;
34956         if(this.cm.isLocked(colIndex)){
34957             tb = this.getLockedTable();
34958             index = colIndex;
34959         }else{
34960             tb = this.getBodyTable();
34961             index = colIndex - this.cm.getLockedCount();
34962         }
34963         if(tb && tb.rows){
34964             var rows = tb.rows;
34965             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
34966             for(var i = 0; i < stopIndex; i++){
34967                 var cell = rows[i].childNodes[index].firstChild;
34968                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
34969             }
34970         }
34971         return maxWidth + /*margin for error in IE*/ 5;
34972     },
34973     /**
34974      * Autofit a column to its content.
34975      * @param {Number} colIndex
34976      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
34977      */
34978      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
34979          if(this.cm.isHidden(colIndex)){
34980              return; // can't calc a hidden column
34981          }
34982         if(forceMinSize){
34983             var cid = this.cm.getColumnId(colIndex);
34984             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
34985            if(this.grid.autoSizeHeaders){
34986                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
34987            }
34988         }
34989         var newWidth = this.calcColumnWidth(colIndex);
34990         this.cm.setColumnWidth(colIndex,
34991             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
34992         if(!suppressEvent){
34993             this.grid.fireEvent("columnresize", colIndex, newWidth);
34994         }
34995     },
34996
34997     /**
34998      * Autofits all columns to their content and then expands to fit any extra space in the grid
34999      */
35000      autoSizeColumns : function(){
35001         var cm = this.grid.colModel;
35002         var colCount = cm.getColumnCount();
35003         for(var i = 0; i < colCount; i++){
35004             this.autoSizeColumn(i, true, true);
35005         }
35006         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
35007             this.fitColumns();
35008         }else{
35009             this.updateColumns();
35010             this.layout();
35011         }
35012     },
35013
35014     /**
35015      * Autofits all columns to the grid's width proportionate with their current size
35016      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
35017      */
35018     fitColumns : function(reserveScrollSpace){
35019         var cm = this.grid.colModel;
35020         var colCount = cm.getColumnCount();
35021         var cols = [];
35022         var width = 0;
35023         var i, w;
35024         for (i = 0; i < colCount; i++){
35025             if(!cm.isHidden(i) && !cm.isFixed(i)){
35026                 w = cm.getColumnWidth(i);
35027                 cols.push(i);
35028                 cols.push(w);
35029                 width += w;
35030             }
35031         }
35032         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
35033         if(reserveScrollSpace){
35034             avail -= 17;
35035         }
35036         var frac = (avail - cm.getTotalWidth())/width;
35037         while (cols.length){
35038             w = cols.pop();
35039             i = cols.pop();
35040             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
35041         }
35042         this.updateColumns();
35043         this.layout();
35044     },
35045
35046     onRowSelect : function(rowIndex){
35047         var row = this.getRowComposite(rowIndex);
35048         row.addClass("x-grid-row-selected");
35049     },
35050
35051     onRowDeselect : function(rowIndex){
35052         var row = this.getRowComposite(rowIndex);
35053         row.removeClass("x-grid-row-selected");
35054     },
35055
35056     onCellSelect : function(row, col){
35057         var cell = this.getCell(row, col);
35058         if(cell){
35059             Roo.fly(cell).addClass("x-grid-cell-selected");
35060         }
35061     },
35062
35063     onCellDeselect : function(row, col){
35064         var cell = this.getCell(row, col);
35065         if(cell){
35066             Roo.fly(cell).removeClass("x-grid-cell-selected");
35067         }
35068     },
35069
35070     updateHeaderSortState : function(){
35071         
35072         // sort state can be single { field: xxx, direction : yyy}
35073         // or   { xxx=>ASC , yyy : DESC ..... }
35074         
35075         var mstate = {};
35076         if (!this.ds.multiSort) { 
35077             var state = this.ds.getSortState();
35078             if(!state){
35079                 return;
35080             }
35081             mstate[state.field] = state.direction;
35082             // FIXME... - this is not used here.. but might be elsewhere..
35083             this.sortState = state;
35084             
35085         } else {
35086             mstate = this.ds.sortToggle;
35087         }
35088         //remove existing sort classes..
35089         
35090         var sc = this.sortClasses;
35091         var hds = this.el.select(this.headerSelector).removeClass(sc);
35092         
35093         for(var f in mstate) {
35094         
35095             var sortColumn = this.cm.findColumnIndex(f);
35096             
35097             if(sortColumn != -1){
35098                 var sortDir = mstate[f];        
35099                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
35100             }
35101         }
35102         
35103          
35104         
35105     },
35106
35107
35108     handleHeaderClick : function(g, index){
35109         if(this.headersDisabled){
35110             return;
35111         }
35112         var dm = g.dataSource, cm = g.colModel;
35113         if(!cm.isSortable(index)){
35114             return;
35115         }
35116         g.stopEditing();
35117         
35118         if (dm.multiSort) {
35119             // update the sortOrder
35120             var so = [];
35121             for(var i = 0; i < cm.config.length; i++ ) {
35122                 
35123                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
35124                     continue; // dont' bother, it's not in sort list or being set.
35125                 }
35126                 
35127                 so.push(cm.config[i].dataIndex);
35128             };
35129             dm.sortOrder = so;
35130         }
35131         
35132         
35133         dm.sort(cm.getDataIndex(index));
35134     },
35135
35136
35137     destroy : function(){
35138         if(this.colMenu){
35139             this.colMenu.removeAll();
35140             Roo.menu.MenuMgr.unregister(this.colMenu);
35141             this.colMenu.getEl().remove();
35142             delete this.colMenu;
35143         }
35144         if(this.hmenu){
35145             this.hmenu.removeAll();
35146             Roo.menu.MenuMgr.unregister(this.hmenu);
35147             this.hmenu.getEl().remove();
35148             delete this.hmenu;
35149         }
35150         if(this.grid.enableColumnMove){
35151             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
35152             if(dds){
35153                 for(var dd in dds){
35154                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
35155                         var elid = dds[dd].dragElId;
35156                         dds[dd].unreg();
35157                         Roo.get(elid).remove();
35158                     } else if(dds[dd].config.isTarget){
35159                         dds[dd].proxyTop.remove();
35160                         dds[dd].proxyBottom.remove();
35161                         dds[dd].unreg();
35162                     }
35163                     if(Roo.dd.DDM.locationCache[dd]){
35164                         delete Roo.dd.DDM.locationCache[dd];
35165                     }
35166                 }
35167                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
35168             }
35169         }
35170         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
35171         this.bind(null, null);
35172         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
35173     },
35174
35175     handleLockChange : function(){
35176         this.refresh(true);
35177     },
35178
35179     onDenyColumnLock : function(){
35180
35181     },
35182
35183     onDenyColumnHide : function(){
35184
35185     },
35186
35187     handleHdMenuClick : function(item){
35188         var index = this.hdCtxIndex;
35189         var cm = this.cm, ds = this.ds;
35190         switch(item.id){
35191             case "asc":
35192                 ds.sort(cm.getDataIndex(index), "ASC");
35193                 break;
35194             case "desc":
35195                 ds.sort(cm.getDataIndex(index), "DESC");
35196                 break;
35197             case "lock":
35198                 var lc = cm.getLockedCount();
35199                 if(cm.getColumnCount(true) <= lc+1){
35200                     this.onDenyColumnLock();
35201                     return;
35202                 }
35203                 if(lc != index){
35204                     cm.setLocked(index, true, true);
35205                     cm.moveColumn(index, lc);
35206                     this.grid.fireEvent("columnmove", index, lc);
35207                 }else{
35208                     cm.setLocked(index, true);
35209                 }
35210             break;
35211             case "unlock":
35212                 var lc = cm.getLockedCount();
35213                 if((lc-1) != index){
35214                     cm.setLocked(index, false, true);
35215                     cm.moveColumn(index, lc-1);
35216                     this.grid.fireEvent("columnmove", index, lc-1);
35217                 }else{
35218                     cm.setLocked(index, false);
35219                 }
35220             break;
35221             default:
35222                 index = cm.getIndexById(item.id.substr(4));
35223                 if(index != -1){
35224                     if(item.checked && cm.getColumnCount(true) <= 1){
35225                         this.onDenyColumnHide();
35226                         return false;
35227                     }
35228                     cm.setHidden(index, item.checked);
35229                 }
35230         }
35231         return true;
35232     },
35233
35234     beforeColMenuShow : function(){
35235         var cm = this.cm,  colCount = cm.getColumnCount();
35236         this.colMenu.removeAll();
35237         for(var i = 0; i < colCount; i++){
35238             this.colMenu.add(new Roo.menu.CheckItem({
35239                 id: "col-"+cm.getColumnId(i),
35240                 text: cm.getColumnHeader(i),
35241                 checked: !cm.isHidden(i),
35242                 hideOnClick:false
35243             }));
35244         }
35245     },
35246
35247     handleHdCtx : function(g, index, e){
35248         e.stopEvent();
35249         var hd = this.getHeaderCell(index);
35250         this.hdCtxIndex = index;
35251         var ms = this.hmenu.items, cm = this.cm;
35252         ms.get("asc").setDisabled(!cm.isSortable(index));
35253         ms.get("desc").setDisabled(!cm.isSortable(index));
35254         if(this.grid.enableColLock !== false){
35255             ms.get("lock").setDisabled(cm.isLocked(index));
35256             ms.get("unlock").setDisabled(!cm.isLocked(index));
35257         }
35258         this.hmenu.show(hd, "tl-bl");
35259     },
35260
35261     handleHdOver : function(e){
35262         var hd = this.findHeaderCell(e.getTarget());
35263         if(hd && !this.headersDisabled){
35264             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
35265                this.fly(hd).addClass("x-grid-hd-over");
35266             }
35267         }
35268     },
35269
35270     handleHdOut : function(e){
35271         var hd = this.findHeaderCell(e.getTarget());
35272         if(hd){
35273             this.fly(hd).removeClass("x-grid-hd-over");
35274         }
35275     },
35276
35277     handleSplitDblClick : function(e, t){
35278         var i = this.getCellIndex(t);
35279         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
35280             this.autoSizeColumn(i, true);
35281             this.layout();
35282         }
35283     },
35284
35285     render : function(){
35286
35287         var cm = this.cm;
35288         var colCount = cm.getColumnCount();
35289
35290         if(this.grid.monitorWindowResize === true){
35291             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35292         }
35293         var header = this.renderHeaders();
35294         var body = this.templates.body.apply({rows:""});
35295         var html = this.templates.master.apply({
35296             lockedBody: body,
35297             body: body,
35298             lockedHeader: header[0],
35299             header: header[1]
35300         });
35301
35302         //this.updateColumns();
35303
35304         this.grid.getGridEl().dom.innerHTML = html;
35305
35306         this.initElements();
35307         
35308         // a kludge to fix the random scolling effect in webkit
35309         this.el.on("scroll", function() {
35310             this.el.dom.scrollTop=0; // hopefully not recursive..
35311         },this);
35312
35313         this.scroller.on("scroll", this.handleScroll, this);
35314         this.lockedBody.on("mousewheel", this.handleWheel, this);
35315         this.mainBody.on("mousewheel", this.handleWheel, this);
35316
35317         this.mainHd.on("mouseover", this.handleHdOver, this);
35318         this.mainHd.on("mouseout", this.handleHdOut, this);
35319         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
35320                 {delegate: "."+this.splitClass});
35321
35322         this.lockedHd.on("mouseover", this.handleHdOver, this);
35323         this.lockedHd.on("mouseout", this.handleHdOut, this);
35324         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
35325                 {delegate: "."+this.splitClass});
35326
35327         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
35328             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
35329         }
35330
35331         this.updateSplitters();
35332
35333         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
35334             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
35335             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
35336         }
35337
35338         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
35339             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
35340             this.hmenu.add(
35341                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
35342                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
35343             );
35344             if(this.grid.enableColLock !== false){
35345                 this.hmenu.add('-',
35346                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
35347                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
35348                 );
35349             }
35350             if(this.grid.enableColumnHide !== false){
35351
35352                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
35353                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
35354                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
35355
35356                 this.hmenu.add('-',
35357                     {id:"columns", text: this.columnsText, menu: this.colMenu}
35358                 );
35359             }
35360             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
35361
35362             this.grid.on("headercontextmenu", this.handleHdCtx, this);
35363         }
35364
35365         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
35366             this.dd = new Roo.grid.GridDragZone(this.grid, {
35367                 ddGroup : this.grid.ddGroup || 'GridDD'
35368             });
35369         }
35370
35371         /*
35372         for(var i = 0; i < colCount; i++){
35373             if(cm.isHidden(i)){
35374                 this.hideColumn(i);
35375             }
35376             if(cm.config[i].align){
35377                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
35378                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
35379             }
35380         }*/
35381         
35382         this.updateHeaderSortState();
35383
35384         this.beforeInitialResize();
35385         this.layout(true);
35386
35387         // two part rendering gives faster view to the user
35388         this.renderPhase2.defer(1, this);
35389     },
35390
35391     renderPhase2 : function(){
35392         // render the rows now
35393         this.refresh();
35394         if(this.grid.autoSizeColumns){
35395             this.autoSizeColumns();
35396         }
35397     },
35398
35399     beforeInitialResize : function(){
35400
35401     },
35402
35403     onColumnSplitterMoved : function(i, w){
35404         this.userResized = true;
35405         var cm = this.grid.colModel;
35406         cm.setColumnWidth(i, w, true);
35407         var cid = cm.getColumnId(i);
35408         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
35409         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
35410         this.updateSplitters();
35411         this.layout();
35412         this.grid.fireEvent("columnresize", i, w);
35413     },
35414
35415     syncRowHeights : function(startIndex, endIndex){
35416         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
35417             startIndex = startIndex || 0;
35418             var mrows = this.getBodyTable().rows;
35419             var lrows = this.getLockedTable().rows;
35420             var len = mrows.length-1;
35421             endIndex = Math.min(endIndex || len, len);
35422             for(var i = startIndex; i <= endIndex; i++){
35423                 var m = mrows[i], l = lrows[i];
35424                 var h = Math.max(m.offsetHeight, l.offsetHeight);
35425                 m.style.height = l.style.height = h + "px";
35426             }
35427         }
35428     },
35429
35430     layout : function(initialRender, is2ndPass){
35431         var g = this.grid;
35432         var auto = g.autoHeight;
35433         var scrollOffset = 16;
35434         var c = g.getGridEl(), cm = this.cm,
35435                 expandCol = g.autoExpandColumn,
35436                 gv = this;
35437         //c.beginMeasure();
35438
35439         if(!c.dom.offsetWidth){ // display:none?
35440             if(initialRender){
35441                 this.lockedWrap.show();
35442                 this.mainWrap.show();
35443             }
35444             return;
35445         }
35446
35447         var hasLock = this.cm.isLocked(0);
35448
35449         var tbh = this.headerPanel.getHeight();
35450         var bbh = this.footerPanel.getHeight();
35451
35452         if(auto){
35453             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
35454             var newHeight = ch + c.getBorderWidth("tb");
35455             if(g.maxHeight){
35456                 newHeight = Math.min(g.maxHeight, newHeight);
35457             }
35458             c.setHeight(newHeight);
35459         }
35460
35461         if(g.autoWidth){
35462             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
35463         }
35464
35465         var s = this.scroller;
35466
35467         var csize = c.getSize(true);
35468
35469         this.el.setSize(csize.width, csize.height);
35470
35471         this.headerPanel.setWidth(csize.width);
35472         this.footerPanel.setWidth(csize.width);
35473
35474         var hdHeight = this.mainHd.getHeight();
35475         var vw = csize.width;
35476         var vh = csize.height - (tbh + bbh);
35477
35478         s.setSize(vw, vh);
35479
35480         var bt = this.getBodyTable();
35481         var ltWidth = hasLock ?
35482                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
35483
35484         var scrollHeight = bt.offsetHeight;
35485         var scrollWidth = ltWidth + bt.offsetWidth;
35486         var vscroll = false, hscroll = false;
35487
35488         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
35489
35490         var lw = this.lockedWrap, mw = this.mainWrap;
35491         var lb = this.lockedBody, mb = this.mainBody;
35492
35493         setTimeout(function(){
35494             var t = s.dom.offsetTop;
35495             var w = s.dom.clientWidth,
35496                 h = s.dom.clientHeight;
35497
35498             lw.setTop(t);
35499             lw.setSize(ltWidth, h);
35500
35501             mw.setLeftTop(ltWidth, t);
35502             mw.setSize(w-ltWidth, h);
35503
35504             lb.setHeight(h-hdHeight);
35505             mb.setHeight(h-hdHeight);
35506
35507             if(is2ndPass !== true && !gv.userResized && expandCol){
35508                 // high speed resize without full column calculation
35509                 
35510                 var ci = cm.getIndexById(expandCol);
35511                 if (ci < 0) {
35512                     ci = cm.findColumnIndex(expandCol);
35513                 }
35514                 ci = Math.max(0, ci); // make sure it's got at least the first col.
35515                 var expandId = cm.getColumnId(ci);
35516                 var  tw = cm.getTotalWidth(false);
35517                 var currentWidth = cm.getColumnWidth(ci);
35518                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
35519                 if(currentWidth != cw){
35520                     cm.setColumnWidth(ci, cw, true);
35521                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
35522                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
35523                     gv.updateSplitters();
35524                     gv.layout(false, true);
35525                 }
35526             }
35527
35528             if(initialRender){
35529                 lw.show();
35530                 mw.show();
35531             }
35532             //c.endMeasure();
35533         }, 10);
35534     },
35535
35536     onWindowResize : function(){
35537         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
35538             return;
35539         }
35540         this.layout();
35541     },
35542
35543     appendFooter : function(parentEl){
35544         return null;
35545     },
35546
35547     sortAscText : "Sort Ascending",
35548     sortDescText : "Sort Descending",
35549     lockText : "Lock Column",
35550     unlockText : "Unlock Column",
35551     columnsText : "Columns"
35552 });
35553
35554
35555 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
35556     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
35557     this.proxy.el.addClass('x-grid3-col-dd');
35558 };
35559
35560 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
35561     handleMouseDown : function(e){
35562
35563     },
35564
35565     callHandleMouseDown : function(e){
35566         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
35567     }
35568 });
35569 /*
35570  * Based on:
35571  * Ext JS Library 1.1.1
35572  * Copyright(c) 2006-2007, Ext JS, LLC.
35573  *
35574  * Originally Released Under LGPL - original licence link has changed is not relivant.
35575  *
35576  * Fork - LGPL
35577  * <script type="text/javascript">
35578  */
35579  
35580 // private
35581 // This is a support class used internally by the Grid components
35582 Roo.grid.SplitDragZone = function(grid, hd, hd2){
35583     this.grid = grid;
35584     this.view = grid.getView();
35585     this.proxy = this.view.resizeProxy;
35586     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
35587         "gridSplitters" + this.grid.getGridEl().id, {
35588         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
35589     });
35590     this.setHandleElId(Roo.id(hd));
35591     this.setOuterHandleElId(Roo.id(hd2));
35592     this.scroll = false;
35593 };
35594 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
35595     fly: Roo.Element.fly,
35596
35597     b4StartDrag : function(x, y){
35598         this.view.headersDisabled = true;
35599         this.proxy.setHeight(this.view.mainWrap.getHeight());
35600         var w = this.cm.getColumnWidth(this.cellIndex);
35601         var minw = Math.max(w-this.grid.minColumnWidth, 0);
35602         this.resetConstraints();
35603         this.setXConstraint(minw, 1000);
35604         this.setYConstraint(0, 0);
35605         this.minX = x - minw;
35606         this.maxX = x + 1000;
35607         this.startPos = x;
35608         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
35609     },
35610
35611
35612     handleMouseDown : function(e){
35613         ev = Roo.EventObject.setEvent(e);
35614         var t = this.fly(ev.getTarget());
35615         if(t.hasClass("x-grid-split")){
35616             this.cellIndex = this.view.getCellIndex(t.dom);
35617             this.split = t.dom;
35618             this.cm = this.grid.colModel;
35619             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
35620                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
35621             }
35622         }
35623     },
35624
35625     endDrag : function(e){
35626         this.view.headersDisabled = false;
35627         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
35628         var diff = endX - this.startPos;
35629         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
35630     },
35631
35632     autoOffset : function(){
35633         this.setDelta(0,0);
35634     }
35635 });/*
35636  * Based on:
35637  * Ext JS Library 1.1.1
35638  * Copyright(c) 2006-2007, Ext JS, LLC.
35639  *
35640  * Originally Released Under LGPL - original licence link has changed is not relivant.
35641  *
35642  * Fork - LGPL
35643  * <script type="text/javascript">
35644  */
35645  
35646 // private
35647 // This is a support class used internally by the Grid components
35648 Roo.grid.GridDragZone = function(grid, config){
35649     this.view = grid.getView();
35650     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
35651     if(this.view.lockedBody){
35652         this.setHandleElId(Roo.id(this.view.mainBody.dom));
35653         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
35654     }
35655     this.scroll = false;
35656     this.grid = grid;
35657     this.ddel = document.createElement('div');
35658     this.ddel.className = 'x-grid-dd-wrap';
35659 };
35660
35661 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
35662     ddGroup : "GridDD",
35663
35664     getDragData : function(e){
35665         var t = Roo.lib.Event.getTarget(e);
35666         var rowIndex = this.view.findRowIndex(t);
35667         if(rowIndex !== false){
35668             var sm = this.grid.selModel;
35669             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
35670               //  sm.mouseDown(e, t);
35671             //}
35672             if (e.hasModifier()){
35673                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
35674             }
35675             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
35676         }
35677         return false;
35678     },
35679
35680     onInitDrag : function(e){
35681         var data = this.dragData;
35682         this.ddel.innerHTML = this.grid.getDragDropText();
35683         this.proxy.update(this.ddel);
35684         // fire start drag?
35685     },
35686
35687     afterRepair : function(){
35688         this.dragging = false;
35689     },
35690
35691     getRepairXY : function(e, data){
35692         return false;
35693     },
35694
35695     onEndDrag : function(data, e){
35696         // fire end drag?
35697     },
35698
35699     onValidDrop : function(dd, e, id){
35700         // fire drag drop?
35701         this.hideProxy();
35702     },
35703
35704     beforeInvalidDrop : function(e, id){
35705
35706     }
35707 });/*
35708  * Based on:
35709  * Ext JS Library 1.1.1
35710  * Copyright(c) 2006-2007, Ext JS, LLC.
35711  *
35712  * Originally Released Under LGPL - original licence link has changed is not relivant.
35713  *
35714  * Fork - LGPL
35715  * <script type="text/javascript">
35716  */
35717  
35718
35719 /**
35720  * @class Roo.grid.ColumnModel
35721  * @extends Roo.util.Observable
35722  * This is the default implementation of a ColumnModel used by the Grid. It defines
35723  * the columns in the grid.
35724  * <br>Usage:<br>
35725  <pre><code>
35726  var colModel = new Roo.grid.ColumnModel([
35727         {header: "Ticker", width: 60, sortable: true, locked: true},
35728         {header: "Company Name", width: 150, sortable: true},
35729         {header: "Market Cap.", width: 100, sortable: true},
35730         {header: "$ Sales", width: 100, sortable: true, renderer: money},
35731         {header: "Employees", width: 100, sortable: true, resizable: false}
35732  ]);
35733  </code></pre>
35734  * <p>
35735  
35736  * The config options listed for this class are options which may appear in each
35737  * individual column definition.
35738  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
35739  * @constructor
35740  * @param {Object} config An Array of column config objects. See this class's
35741  * config objects for details.
35742 */
35743 Roo.grid.ColumnModel = function(config){
35744         /**
35745      * The config passed into the constructor
35746      */
35747     this.config = config;
35748     this.lookup = {};
35749
35750     // if no id, create one
35751     // if the column does not have a dataIndex mapping,
35752     // map it to the order it is in the config
35753     for(var i = 0, len = config.length; i < len; i++){
35754         var c = config[i];
35755         if(typeof c.dataIndex == "undefined"){
35756             c.dataIndex = i;
35757         }
35758         if(typeof c.renderer == "string"){
35759             c.renderer = Roo.util.Format[c.renderer];
35760         }
35761         if(typeof c.id == "undefined"){
35762             c.id = Roo.id();
35763         }
35764         if(c.editor && c.editor.xtype){
35765             c.editor  = Roo.factory(c.editor, Roo.grid);
35766         }
35767         if(c.editor && c.editor.isFormField){
35768             c.editor = new Roo.grid.GridEditor(c.editor);
35769         }
35770         this.lookup[c.id] = c;
35771     }
35772
35773     /**
35774      * The width of columns which have no width specified (defaults to 100)
35775      * @type Number
35776      */
35777     this.defaultWidth = 100;
35778
35779     /**
35780      * Default sortable of columns which have no sortable specified (defaults to false)
35781      * @type Boolean
35782      */
35783     this.defaultSortable = false;
35784
35785     this.addEvents({
35786         /**
35787              * @event widthchange
35788              * Fires when the width of a column changes.
35789              * @param {ColumnModel} this
35790              * @param {Number} columnIndex The column index
35791              * @param {Number} newWidth The new width
35792              */
35793             "widthchange": true,
35794         /**
35795              * @event headerchange
35796              * Fires when the text of a header changes.
35797              * @param {ColumnModel} this
35798              * @param {Number} columnIndex The column index
35799              * @param {Number} newText The new header text
35800              */
35801             "headerchange": true,
35802         /**
35803              * @event hiddenchange
35804              * Fires when a column is hidden or "unhidden".
35805              * @param {ColumnModel} this
35806              * @param {Number} columnIndex The column index
35807              * @param {Boolean} hidden true if hidden, false otherwise
35808              */
35809             "hiddenchange": true,
35810             /**
35811          * @event columnmoved
35812          * Fires when a column is moved.
35813          * @param {ColumnModel} this
35814          * @param {Number} oldIndex
35815          * @param {Number} newIndex
35816          */
35817         "columnmoved" : true,
35818         /**
35819          * @event columlockchange
35820          * Fires when a column's locked state is changed
35821          * @param {ColumnModel} this
35822          * @param {Number} colIndex
35823          * @param {Boolean} locked true if locked
35824          */
35825         "columnlockchange" : true
35826     });
35827     Roo.grid.ColumnModel.superclass.constructor.call(this);
35828 };
35829 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
35830     /**
35831      * @cfg {String} header The header text to display in the Grid view.
35832      */
35833     /**
35834      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
35835      * {@link Roo.data.Record} definition from which to draw the column's value. If not
35836      * specified, the column's index is used as an index into the Record's data Array.
35837      */
35838     /**
35839      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
35840      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
35841      */
35842     /**
35843      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
35844      * Defaults to the value of the {@link #defaultSortable} property.
35845      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
35846      */
35847     /**
35848      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
35849      */
35850     /**
35851      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
35852      */
35853     /**
35854      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
35855      */
35856     /**
35857      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
35858      */
35859     /**
35860      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
35861      * given the cell's data value. See {@link #setRenderer}. If not specified, the
35862      * default renderer uses the raw data value.
35863      */
35864        /**
35865      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
35866      */
35867     /**
35868      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
35869      */
35870
35871     /**
35872      * Returns the id of the column at the specified index.
35873      * @param {Number} index The column index
35874      * @return {String} the id
35875      */
35876     getColumnId : function(index){
35877         return this.config[index].id;
35878     },
35879
35880     /**
35881      * Returns the column for a specified id.
35882      * @param {String} id The column id
35883      * @return {Object} the column
35884      */
35885     getColumnById : function(id){
35886         return this.lookup[id];
35887     },
35888
35889     
35890     /**
35891      * Returns the column for a specified dataIndex.
35892      * @param {String} dataIndex The column dataIndex
35893      * @return {Object|Boolean} the column or false if not found
35894      */
35895     getColumnByDataIndex: function(dataIndex){
35896         var index = this.findColumnIndex(dataIndex);
35897         return index > -1 ? this.config[index] : false;
35898     },
35899     
35900     /**
35901      * Returns the index for a specified column id.
35902      * @param {String} id The column id
35903      * @return {Number} the index, or -1 if not found
35904      */
35905     getIndexById : function(id){
35906         for(var i = 0, len = this.config.length; i < len; i++){
35907             if(this.config[i].id == id){
35908                 return i;
35909             }
35910         }
35911         return -1;
35912     },
35913     
35914     /**
35915      * Returns the index for a specified column dataIndex.
35916      * @param {String} dataIndex The column dataIndex
35917      * @return {Number} the index, or -1 if not found
35918      */
35919     
35920     findColumnIndex : function(dataIndex){
35921         for(var i = 0, len = this.config.length; i < len; i++){
35922             if(this.config[i].dataIndex == dataIndex){
35923                 return i;
35924             }
35925         }
35926         return -1;
35927     },
35928     
35929     
35930     moveColumn : function(oldIndex, newIndex){
35931         var c = this.config[oldIndex];
35932         this.config.splice(oldIndex, 1);
35933         this.config.splice(newIndex, 0, c);
35934         this.dataMap = null;
35935         this.fireEvent("columnmoved", this, oldIndex, newIndex);
35936     },
35937
35938     isLocked : function(colIndex){
35939         return this.config[colIndex].locked === true;
35940     },
35941
35942     setLocked : function(colIndex, value, suppressEvent){
35943         if(this.isLocked(colIndex) == value){
35944             return;
35945         }
35946         this.config[colIndex].locked = value;
35947         if(!suppressEvent){
35948             this.fireEvent("columnlockchange", this, colIndex, value);
35949         }
35950     },
35951
35952     getTotalLockedWidth : function(){
35953         var totalWidth = 0;
35954         for(var i = 0; i < this.config.length; i++){
35955             if(this.isLocked(i) && !this.isHidden(i)){
35956                 this.totalWidth += this.getColumnWidth(i);
35957             }
35958         }
35959         return totalWidth;
35960     },
35961
35962     getLockedCount : function(){
35963         for(var i = 0, len = this.config.length; i < len; i++){
35964             if(!this.isLocked(i)){
35965                 return i;
35966             }
35967         }
35968     },
35969
35970     /**
35971      * Returns the number of columns.
35972      * @return {Number}
35973      */
35974     getColumnCount : function(visibleOnly){
35975         if(visibleOnly === true){
35976             var c = 0;
35977             for(var i = 0, len = this.config.length; i < len; i++){
35978                 if(!this.isHidden(i)){
35979                     c++;
35980                 }
35981             }
35982             return c;
35983         }
35984         return this.config.length;
35985     },
35986
35987     /**
35988      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
35989      * @param {Function} fn
35990      * @param {Object} scope (optional)
35991      * @return {Array} result
35992      */
35993     getColumnsBy : function(fn, scope){
35994         var r = [];
35995         for(var i = 0, len = this.config.length; i < len; i++){
35996             var c = this.config[i];
35997             if(fn.call(scope||this, c, i) === true){
35998                 r[r.length] = c;
35999             }
36000         }
36001         return r;
36002     },
36003
36004     /**
36005      * Returns true if the specified column is sortable.
36006      * @param {Number} col The column index
36007      * @return {Boolean}
36008      */
36009     isSortable : function(col){
36010         if(typeof this.config[col].sortable == "undefined"){
36011             return this.defaultSortable;
36012         }
36013         return this.config[col].sortable;
36014     },
36015
36016     /**
36017      * Returns the rendering (formatting) function defined for the column.
36018      * @param {Number} col The column index.
36019      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
36020      */
36021     getRenderer : function(col){
36022         if(!this.config[col].renderer){
36023             return Roo.grid.ColumnModel.defaultRenderer;
36024         }
36025         return this.config[col].renderer;
36026     },
36027
36028     /**
36029      * Sets the rendering (formatting) function for a column.
36030      * @param {Number} col The column index
36031      * @param {Function} fn The function to use to process the cell's raw data
36032      * to return HTML markup for the grid view. The render function is called with
36033      * the following parameters:<ul>
36034      * <li>Data value.</li>
36035      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
36036      * <li>css A CSS style string to apply to the table cell.</li>
36037      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
36038      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
36039      * <li>Row index</li>
36040      * <li>Column index</li>
36041      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
36042      */
36043     setRenderer : function(col, fn){
36044         this.config[col].renderer = fn;
36045     },
36046
36047     /**
36048      * Returns the width for the specified column.
36049      * @param {Number} col The column index
36050      * @return {Number}
36051      */
36052     getColumnWidth : function(col){
36053         return this.config[col].width * 1 || this.defaultWidth;
36054     },
36055
36056     /**
36057      * Sets the width for a column.
36058      * @param {Number} col The column index
36059      * @param {Number} width The new width
36060      */
36061     setColumnWidth : function(col, width, suppressEvent){
36062         this.config[col].width = width;
36063         this.totalWidth = null;
36064         if(!suppressEvent){
36065              this.fireEvent("widthchange", this, col, width);
36066         }
36067     },
36068
36069     /**
36070      * Returns the total width of all columns.
36071      * @param {Boolean} includeHidden True to include hidden column widths
36072      * @return {Number}
36073      */
36074     getTotalWidth : function(includeHidden){
36075         if(!this.totalWidth){
36076             this.totalWidth = 0;
36077             for(var i = 0, len = this.config.length; i < len; i++){
36078                 if(includeHidden || !this.isHidden(i)){
36079                     this.totalWidth += this.getColumnWidth(i);
36080                 }
36081             }
36082         }
36083         return this.totalWidth;
36084     },
36085
36086     /**
36087      * Returns the header for the specified column.
36088      * @param {Number} col The column index
36089      * @return {String}
36090      */
36091     getColumnHeader : function(col){
36092         return this.config[col].header;
36093     },
36094
36095     /**
36096      * Sets the header for a column.
36097      * @param {Number} col The column index
36098      * @param {String} header The new header
36099      */
36100     setColumnHeader : function(col, header){
36101         this.config[col].header = header;
36102         this.fireEvent("headerchange", this, col, header);
36103     },
36104
36105     /**
36106      * Returns the tooltip for the specified column.
36107      * @param {Number} col The column index
36108      * @return {String}
36109      */
36110     getColumnTooltip : function(col){
36111             return this.config[col].tooltip;
36112     },
36113     /**
36114      * Sets the tooltip for a column.
36115      * @param {Number} col The column index
36116      * @param {String} tooltip The new tooltip
36117      */
36118     setColumnTooltip : function(col, tooltip){
36119             this.config[col].tooltip = tooltip;
36120     },
36121
36122     /**
36123      * Returns the dataIndex for the specified column.
36124      * @param {Number} col The column index
36125      * @return {Number}
36126      */
36127     getDataIndex : function(col){
36128         return this.config[col].dataIndex;
36129     },
36130
36131     /**
36132      * Sets the dataIndex for a column.
36133      * @param {Number} col The column index
36134      * @param {Number} dataIndex The new dataIndex
36135      */
36136     setDataIndex : function(col, dataIndex){
36137         this.config[col].dataIndex = dataIndex;
36138     },
36139
36140     
36141     
36142     /**
36143      * Returns true if the cell is editable.
36144      * @param {Number} colIndex The column index
36145      * @param {Number} rowIndex The row index
36146      * @return {Boolean}
36147      */
36148     isCellEditable : function(colIndex, rowIndex){
36149         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
36150     },
36151
36152     /**
36153      * Returns the editor defined for the cell/column.
36154      * return false or null to disable editing.
36155      * @param {Number} colIndex The column index
36156      * @param {Number} rowIndex The row index
36157      * @return {Object}
36158      */
36159     getCellEditor : function(colIndex, rowIndex){
36160         return this.config[colIndex].editor;
36161     },
36162
36163     /**
36164      * Sets if a column is editable.
36165      * @param {Number} col The column index
36166      * @param {Boolean} editable True if the column is editable
36167      */
36168     setEditable : function(col, editable){
36169         this.config[col].editable = editable;
36170     },
36171
36172
36173     /**
36174      * Returns true if the column is hidden.
36175      * @param {Number} colIndex The column index
36176      * @return {Boolean}
36177      */
36178     isHidden : function(colIndex){
36179         return this.config[colIndex].hidden;
36180     },
36181
36182
36183     /**
36184      * Returns true if the column width cannot be changed
36185      */
36186     isFixed : function(colIndex){
36187         return this.config[colIndex].fixed;
36188     },
36189
36190     /**
36191      * Returns true if the column can be resized
36192      * @return {Boolean}
36193      */
36194     isResizable : function(colIndex){
36195         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
36196     },
36197     /**
36198      * Sets if a column is hidden.
36199      * @param {Number} colIndex The column index
36200      * @param {Boolean} hidden True if the column is hidden
36201      */
36202     setHidden : function(colIndex, hidden){
36203         this.config[colIndex].hidden = hidden;
36204         this.totalWidth = null;
36205         this.fireEvent("hiddenchange", this, colIndex, hidden);
36206     },
36207
36208     /**
36209      * Sets the editor for a column.
36210      * @param {Number} col The column index
36211      * @param {Object} editor The editor object
36212      */
36213     setEditor : function(col, editor){
36214         this.config[col].editor = editor;
36215     }
36216 });
36217
36218 Roo.grid.ColumnModel.defaultRenderer = function(value){
36219         if(typeof value == "string" && value.length < 1){
36220             return "&#160;";
36221         }
36222         return value;
36223 };
36224
36225 // Alias for backwards compatibility
36226 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
36227 /*
36228  * Based on:
36229  * Ext JS Library 1.1.1
36230  * Copyright(c) 2006-2007, Ext JS, LLC.
36231  *
36232  * Originally Released Under LGPL - original licence link has changed is not relivant.
36233  *
36234  * Fork - LGPL
36235  * <script type="text/javascript">
36236  */
36237
36238 /**
36239  * @class Roo.grid.AbstractSelectionModel
36240  * @extends Roo.util.Observable
36241  * Abstract base class for grid SelectionModels.  It provides the interface that should be
36242  * implemented by descendant classes.  This class should not be directly instantiated.
36243  * @constructor
36244  */
36245 Roo.grid.AbstractSelectionModel = function(){
36246     this.locked = false;
36247     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
36248 };
36249
36250 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
36251     /** @ignore Called by the grid automatically. Do not call directly. */
36252     init : function(grid){
36253         this.grid = grid;
36254         this.initEvents();
36255     },
36256
36257     /**
36258      * Locks the selections.
36259      */
36260     lock : function(){
36261         this.locked = true;
36262     },
36263
36264     /**
36265      * Unlocks the selections.
36266      */
36267     unlock : function(){
36268         this.locked = false;
36269     },
36270
36271     /**
36272      * Returns true if the selections are locked.
36273      * @return {Boolean}
36274      */
36275     isLocked : function(){
36276         return this.locked;
36277     }
36278 });/*
36279  * Based on:
36280  * Ext JS Library 1.1.1
36281  * Copyright(c) 2006-2007, Ext JS, LLC.
36282  *
36283  * Originally Released Under LGPL - original licence link has changed is not relivant.
36284  *
36285  * Fork - LGPL
36286  * <script type="text/javascript">
36287  */
36288 /**
36289  * @extends Roo.grid.AbstractSelectionModel
36290  * @class Roo.grid.RowSelectionModel
36291  * The default SelectionModel used by {@link Roo.grid.Grid}.
36292  * It supports multiple selections and keyboard selection/navigation. 
36293  * @constructor
36294  * @param {Object} config
36295  */
36296 Roo.grid.RowSelectionModel = function(config){
36297     Roo.apply(this, config);
36298     this.selections = new Roo.util.MixedCollection(false, function(o){
36299         return o.id;
36300     });
36301
36302     this.last = false;
36303     this.lastActive = false;
36304
36305     this.addEvents({
36306         /**
36307              * @event selectionchange
36308              * Fires when the selection changes
36309              * @param {SelectionModel} this
36310              */
36311             "selectionchange" : true,
36312         /**
36313              * @event afterselectionchange
36314              * Fires after the selection changes (eg. by key press or clicking)
36315              * @param {SelectionModel} this
36316              */
36317             "afterselectionchange" : true,
36318         /**
36319              * @event beforerowselect
36320              * Fires when a row is selected being selected, return false to cancel.
36321              * @param {SelectionModel} this
36322              * @param {Number} rowIndex The selected index
36323              * @param {Boolean} keepExisting False if other selections will be cleared
36324              */
36325             "beforerowselect" : true,
36326         /**
36327              * @event rowselect
36328              * Fires when a row is selected.
36329              * @param {SelectionModel} this
36330              * @param {Number} rowIndex The selected index
36331              * @param {Roo.data.Record} r The record
36332              */
36333             "rowselect" : true,
36334         /**
36335              * @event rowdeselect
36336              * Fires when a row is deselected.
36337              * @param {SelectionModel} this
36338              * @param {Number} rowIndex The selected index
36339              */
36340         "rowdeselect" : true
36341     });
36342     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
36343     this.locked = false;
36344 };
36345
36346 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
36347     /**
36348      * @cfg {Boolean} singleSelect
36349      * True to allow selection of only one row at a time (defaults to false)
36350      */
36351     singleSelect : false,
36352
36353     // private
36354     initEvents : function(){
36355
36356         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
36357             this.grid.on("mousedown", this.handleMouseDown, this);
36358         }else{ // allow click to work like normal
36359             this.grid.on("rowclick", this.handleDragableRowClick, this);
36360         }
36361
36362         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
36363             "up" : function(e){
36364                 if(!e.shiftKey){
36365                     this.selectPrevious(e.shiftKey);
36366                 }else if(this.last !== false && this.lastActive !== false){
36367                     var last = this.last;
36368                     this.selectRange(this.last,  this.lastActive-1);
36369                     this.grid.getView().focusRow(this.lastActive);
36370                     if(last !== false){
36371                         this.last = last;
36372                     }
36373                 }else{
36374                     this.selectFirstRow();
36375                 }
36376                 this.fireEvent("afterselectionchange", this);
36377             },
36378             "down" : function(e){
36379                 if(!e.shiftKey){
36380                     this.selectNext(e.shiftKey);
36381                 }else if(this.last !== false && this.lastActive !== false){
36382                     var last = this.last;
36383                     this.selectRange(this.last,  this.lastActive+1);
36384                     this.grid.getView().focusRow(this.lastActive);
36385                     if(last !== false){
36386                         this.last = last;
36387                     }
36388                 }else{
36389                     this.selectFirstRow();
36390                 }
36391                 this.fireEvent("afterselectionchange", this);
36392             },
36393             scope: this
36394         });
36395
36396         var view = this.grid.view;
36397         view.on("refresh", this.onRefresh, this);
36398         view.on("rowupdated", this.onRowUpdated, this);
36399         view.on("rowremoved", this.onRemove, this);
36400     },
36401
36402     // private
36403     onRefresh : function(){
36404         var ds = this.grid.dataSource, i, v = this.grid.view;
36405         var s = this.selections;
36406         s.each(function(r){
36407             if((i = ds.indexOfId(r.id)) != -1){
36408                 v.onRowSelect(i);
36409             }else{
36410                 s.remove(r);
36411             }
36412         });
36413     },
36414
36415     // private
36416     onRemove : function(v, index, r){
36417         this.selections.remove(r);
36418     },
36419
36420     // private
36421     onRowUpdated : function(v, index, r){
36422         if(this.isSelected(r)){
36423             v.onRowSelect(index);
36424         }
36425     },
36426
36427     /**
36428      * Select records.
36429      * @param {Array} records The records to select
36430      * @param {Boolean} keepExisting (optional) True to keep existing selections
36431      */
36432     selectRecords : function(records, keepExisting){
36433         if(!keepExisting){
36434             this.clearSelections();
36435         }
36436         var ds = this.grid.dataSource;
36437         for(var i = 0, len = records.length; i < len; i++){
36438             this.selectRow(ds.indexOf(records[i]), true);
36439         }
36440     },
36441
36442     /**
36443      * Gets the number of selected rows.
36444      * @return {Number}
36445      */
36446     getCount : function(){
36447         return this.selections.length;
36448     },
36449
36450     /**
36451      * Selects the first row in the grid.
36452      */
36453     selectFirstRow : function(){
36454         this.selectRow(0);
36455     },
36456
36457     /**
36458      * Select the last row.
36459      * @param {Boolean} keepExisting (optional) True to keep existing selections
36460      */
36461     selectLastRow : function(keepExisting){
36462         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
36463     },
36464
36465     /**
36466      * Selects the row immediately following the last selected row.
36467      * @param {Boolean} keepExisting (optional) True to keep existing selections
36468      */
36469     selectNext : function(keepExisting){
36470         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
36471             this.selectRow(this.last+1, keepExisting);
36472             this.grid.getView().focusRow(this.last);
36473         }
36474     },
36475
36476     /**
36477      * Selects the row that precedes the last selected row.
36478      * @param {Boolean} keepExisting (optional) True to keep existing selections
36479      */
36480     selectPrevious : function(keepExisting){
36481         if(this.last){
36482             this.selectRow(this.last-1, keepExisting);
36483             this.grid.getView().focusRow(this.last);
36484         }
36485     },
36486
36487     /**
36488      * Returns the selected records
36489      * @return {Array} Array of selected records
36490      */
36491     getSelections : function(){
36492         return [].concat(this.selections.items);
36493     },
36494
36495     /**
36496      * Returns the first selected record.
36497      * @return {Record}
36498      */
36499     getSelected : function(){
36500         return this.selections.itemAt(0);
36501     },
36502
36503
36504     /**
36505      * Clears all selections.
36506      */
36507     clearSelections : function(fast){
36508         if(this.locked) return;
36509         if(fast !== true){
36510             var ds = this.grid.dataSource;
36511             var s = this.selections;
36512             s.each(function(r){
36513                 this.deselectRow(ds.indexOfId(r.id));
36514             }, this);
36515             s.clear();
36516         }else{
36517             this.selections.clear();
36518         }
36519         this.last = false;
36520     },
36521
36522
36523     /**
36524      * Selects all rows.
36525      */
36526     selectAll : function(){
36527         if(this.locked) return;
36528         this.selections.clear();
36529         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
36530             this.selectRow(i, true);
36531         }
36532     },
36533
36534     /**
36535      * Returns True if there is a selection.
36536      * @return {Boolean}
36537      */
36538     hasSelection : function(){
36539         return this.selections.length > 0;
36540     },
36541
36542     /**
36543      * Returns True if the specified row is selected.
36544      * @param {Number/Record} record The record or index of the record to check
36545      * @return {Boolean}
36546      */
36547     isSelected : function(index){
36548         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
36549         return (r && this.selections.key(r.id) ? true : false);
36550     },
36551
36552     /**
36553      * Returns True if the specified record id is selected.
36554      * @param {String} id The id of record to check
36555      * @return {Boolean}
36556      */
36557     isIdSelected : function(id){
36558         return (this.selections.key(id) ? true : false);
36559     },
36560
36561     // private
36562     handleMouseDown : function(e, t){
36563         var view = this.grid.getView(), rowIndex;
36564         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
36565             return;
36566         };
36567         if(e.shiftKey && this.last !== false){
36568             var last = this.last;
36569             this.selectRange(last, rowIndex, e.ctrlKey);
36570             this.last = last; // reset the last
36571             view.focusRow(rowIndex);
36572         }else{
36573             var isSelected = this.isSelected(rowIndex);
36574             if(e.button !== 0 && isSelected){
36575                 view.focusRow(rowIndex);
36576             }else if(e.ctrlKey && isSelected){
36577                 this.deselectRow(rowIndex);
36578             }else if(!isSelected){
36579                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
36580                 view.focusRow(rowIndex);
36581             }
36582         }
36583         this.fireEvent("afterselectionchange", this);
36584     },
36585     // private
36586     handleDragableRowClick :  function(grid, rowIndex, e) 
36587     {
36588         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
36589             this.selectRow(rowIndex, false);
36590             grid.view.focusRow(rowIndex);
36591              this.fireEvent("afterselectionchange", this);
36592         }
36593     },
36594     
36595     /**
36596      * Selects multiple rows.
36597      * @param {Array} rows Array of the indexes of the row to select
36598      * @param {Boolean} keepExisting (optional) True to keep existing selections
36599      */
36600     selectRows : function(rows, keepExisting){
36601         if(!keepExisting){
36602             this.clearSelections();
36603         }
36604         for(var i = 0, len = rows.length; i < len; i++){
36605             this.selectRow(rows[i], true);
36606         }
36607     },
36608
36609     /**
36610      * Selects a range of rows. All rows in between startRow and endRow are also selected.
36611      * @param {Number} startRow The index of the first row in the range
36612      * @param {Number} endRow The index of the last row in the range
36613      * @param {Boolean} keepExisting (optional) True to retain existing selections
36614      */
36615     selectRange : function(startRow, endRow, keepExisting){
36616         if(this.locked) return;
36617         if(!keepExisting){
36618             this.clearSelections();
36619         }
36620         if(startRow <= endRow){
36621             for(var i = startRow; i <= endRow; i++){
36622                 this.selectRow(i, true);
36623             }
36624         }else{
36625             for(var i = startRow; i >= endRow; i--){
36626                 this.selectRow(i, true);
36627             }
36628         }
36629     },
36630
36631     /**
36632      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
36633      * @param {Number} startRow The index of the first row in the range
36634      * @param {Number} endRow The index of the last row in the range
36635      */
36636     deselectRange : function(startRow, endRow, preventViewNotify){
36637         if(this.locked) return;
36638         for(var i = startRow; i <= endRow; i++){
36639             this.deselectRow(i, preventViewNotify);
36640         }
36641     },
36642
36643     /**
36644      * Selects a row.
36645      * @param {Number} row The index of the row to select
36646      * @param {Boolean} keepExisting (optional) True to keep existing selections
36647      */
36648     selectRow : function(index, keepExisting, preventViewNotify){
36649         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
36650         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
36651             if(!keepExisting || this.singleSelect){
36652                 this.clearSelections();
36653             }
36654             var r = this.grid.dataSource.getAt(index);
36655             this.selections.add(r);
36656             this.last = this.lastActive = index;
36657             if(!preventViewNotify){
36658                 this.grid.getView().onRowSelect(index);
36659             }
36660             this.fireEvent("rowselect", this, index, r);
36661             this.fireEvent("selectionchange", this);
36662         }
36663     },
36664
36665     /**
36666      * Deselects a row.
36667      * @param {Number} row The index of the row to deselect
36668      */
36669     deselectRow : function(index, preventViewNotify){
36670         if(this.locked) return;
36671         if(this.last == index){
36672             this.last = false;
36673         }
36674         if(this.lastActive == index){
36675             this.lastActive = false;
36676         }
36677         var r = this.grid.dataSource.getAt(index);
36678         this.selections.remove(r);
36679         if(!preventViewNotify){
36680             this.grid.getView().onRowDeselect(index);
36681         }
36682         this.fireEvent("rowdeselect", this, index);
36683         this.fireEvent("selectionchange", this);
36684     },
36685
36686     // private
36687     restoreLast : function(){
36688         if(this._last){
36689             this.last = this._last;
36690         }
36691     },
36692
36693     // private
36694     acceptsNav : function(row, col, cm){
36695         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36696     },
36697
36698     // private
36699     onEditorKey : function(field, e){
36700         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
36701         if(k == e.TAB){
36702             e.stopEvent();
36703             ed.completeEdit();
36704             if(e.shiftKey){
36705                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36706             }else{
36707                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36708             }
36709         }else if(k == e.ENTER && !e.ctrlKey){
36710             e.stopEvent();
36711             ed.completeEdit();
36712             if(e.shiftKey){
36713                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
36714             }else{
36715                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
36716             }
36717         }else if(k == e.ESC){
36718             ed.cancelEdit();
36719         }
36720         if(newCell){
36721             g.startEditing(newCell[0], newCell[1]);
36722         }
36723     }
36724 });/*
36725  * Based on:
36726  * Ext JS Library 1.1.1
36727  * Copyright(c) 2006-2007, Ext JS, LLC.
36728  *
36729  * Originally Released Under LGPL - original licence link has changed is not relivant.
36730  *
36731  * Fork - LGPL
36732  * <script type="text/javascript">
36733  */
36734 /**
36735  * @class Roo.grid.CellSelectionModel
36736  * @extends Roo.grid.AbstractSelectionModel
36737  * This class provides the basic implementation for cell selection in a grid.
36738  * @constructor
36739  * @param {Object} config The object containing the configuration of this model.
36740  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
36741  */
36742 Roo.grid.CellSelectionModel = function(config){
36743     Roo.apply(this, config);
36744
36745     this.selection = null;
36746
36747     this.addEvents({
36748         /**
36749              * @event beforerowselect
36750              * Fires before a cell is selected.
36751              * @param {SelectionModel} this
36752              * @param {Number} rowIndex The selected row index
36753              * @param {Number} colIndex The selected cell index
36754              */
36755             "beforecellselect" : true,
36756         /**
36757              * @event cellselect
36758              * Fires when a cell is selected.
36759              * @param {SelectionModel} this
36760              * @param {Number} rowIndex The selected row index
36761              * @param {Number} colIndex The selected cell index
36762              */
36763             "cellselect" : true,
36764         /**
36765              * @event selectionchange
36766              * Fires when the active selection changes.
36767              * @param {SelectionModel} this
36768              * @param {Object} selection null for no selection or an object (o) with two properties
36769                 <ul>
36770                 <li>o.record: the record object for the row the selection is in</li>
36771                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
36772                 </ul>
36773              */
36774             "selectionchange" : true,
36775         /**
36776              * @event tabend
36777              * Fires when the tab (or enter) was pressed on the last editable cell
36778              * You can use this to trigger add new row.
36779              * @param {SelectionModel} this
36780              */
36781             "tabend" : true
36782     });
36783     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
36784 };
36785
36786 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
36787     
36788     enter_is_tab: false,
36789
36790     /** @ignore */
36791     initEvents : function(){
36792         this.grid.on("mousedown", this.handleMouseDown, this);
36793         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
36794         var view = this.grid.view;
36795         view.on("refresh", this.onViewChange, this);
36796         view.on("rowupdated", this.onRowUpdated, this);
36797         view.on("beforerowremoved", this.clearSelections, this);
36798         view.on("beforerowsinserted", this.clearSelections, this);
36799         if(this.grid.isEditor){
36800             this.grid.on("beforeedit", this.beforeEdit,  this);
36801         }
36802     },
36803
36804         //private
36805     beforeEdit : function(e){
36806         this.select(e.row, e.column, false, true, e.record);
36807     },
36808
36809         //private
36810     onRowUpdated : function(v, index, r){
36811         if(this.selection && this.selection.record == r){
36812             v.onCellSelect(index, this.selection.cell[1]);
36813         }
36814     },
36815
36816         //private
36817     onViewChange : function(){
36818         this.clearSelections(true);
36819     },
36820
36821         /**
36822          * Returns the currently selected cell,.
36823          * @return {Array} The selected cell (row, column) or null if none selected.
36824          */
36825     getSelectedCell : function(){
36826         return this.selection ? this.selection.cell : null;
36827     },
36828
36829     /**
36830      * Clears all selections.
36831      * @param {Boolean} true to prevent the gridview from being notified about the change.
36832      */
36833     clearSelections : function(preventNotify){
36834         var s = this.selection;
36835         if(s){
36836             if(preventNotify !== true){
36837                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
36838             }
36839             this.selection = null;
36840             this.fireEvent("selectionchange", this, null);
36841         }
36842     },
36843
36844     /**
36845      * Returns true if there is a selection.
36846      * @return {Boolean}
36847      */
36848     hasSelection : function(){
36849         return this.selection ? true : false;
36850     },
36851
36852     /** @ignore */
36853     handleMouseDown : function(e, t){
36854         var v = this.grid.getView();
36855         if(this.isLocked()){
36856             return;
36857         };
36858         var row = v.findRowIndex(t);
36859         var cell = v.findCellIndex(t);
36860         if(row !== false && cell !== false){
36861             this.select(row, cell);
36862         }
36863     },
36864
36865     /**
36866      * Selects a cell.
36867      * @param {Number} rowIndex
36868      * @param {Number} collIndex
36869      */
36870     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
36871         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
36872             this.clearSelections();
36873             r = r || this.grid.dataSource.getAt(rowIndex);
36874             this.selection = {
36875                 record : r,
36876                 cell : [rowIndex, colIndex]
36877             };
36878             if(!preventViewNotify){
36879                 var v = this.grid.getView();
36880                 v.onCellSelect(rowIndex, colIndex);
36881                 if(preventFocus !== true){
36882                     v.focusCell(rowIndex, colIndex);
36883                 }
36884             }
36885             this.fireEvent("cellselect", this, rowIndex, colIndex);
36886             this.fireEvent("selectionchange", this, this.selection);
36887         }
36888     },
36889
36890         //private
36891     isSelectable : function(rowIndex, colIndex, cm){
36892         return !cm.isHidden(colIndex);
36893     },
36894
36895     /** @ignore */
36896     handleKeyDown : function(e){
36897         //Roo.log('Cell Sel Model handleKeyDown');
36898         if(!e.isNavKeyPress()){
36899             return;
36900         }
36901         var g = this.grid, s = this.selection;
36902         if(!s){
36903             e.stopEvent();
36904             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
36905             if(cell){
36906                 this.select(cell[0], cell[1]);
36907             }
36908             return;
36909         }
36910         var sm = this;
36911         var walk = function(row, col, step){
36912             return g.walkCells(row, col, step, sm.isSelectable,  sm);
36913         };
36914         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
36915         var newCell;
36916
36917       
36918
36919         switch(k){
36920             case e.TAB:
36921                 // handled by onEditorKey
36922                 if (g.isEditor && g.editing) {
36923                     return;
36924                 }
36925                 if(e.shiftKey) {
36926                     newCell = walk(r, c-1, -1);
36927                 } else {
36928                     newCell = walk(r, c+1, 1);
36929                 }
36930                 break;
36931             
36932             case e.DOWN:
36933                newCell = walk(r+1, c, 1);
36934                 break;
36935             
36936             case e.UP:
36937                 newCell = walk(r-1, c, -1);
36938                 break;
36939             
36940             case e.RIGHT:
36941                 newCell = walk(r, c+1, 1);
36942                 break;
36943             
36944             case e.LEFT:
36945                 newCell = walk(r, c-1, -1);
36946                 break;
36947             
36948             case e.ENTER:
36949                 
36950                 if(g.isEditor && !g.editing){
36951                    g.startEditing(r, c);
36952                    e.stopEvent();
36953                    return;
36954                 }
36955                 
36956                 
36957              break;
36958         };
36959         if(newCell){
36960             this.select(newCell[0], newCell[1]);
36961             e.stopEvent();
36962             
36963         }
36964     },
36965
36966     acceptsNav : function(row, col, cm){
36967         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36968     },
36969     /**
36970      * Selects a cell.
36971      * @param {Number} field (not used) - as it's normally used as a listener
36972      * @param {Number} e - event - fake it by using
36973      *
36974      * var e = Roo.EventObjectImpl.prototype;
36975      * e.keyCode = e.TAB
36976      *
36977      * 
36978      */
36979     onEditorKey : function(field, e){
36980         
36981         var k = e.getKey(),
36982             newCell,
36983             g = this.grid,
36984             ed = g.activeEditor,
36985             forward = false;
36986         ///Roo.log('onEditorKey' + k);
36987         
36988         
36989         if (this.enter_is_tab && k == e.ENTER) {
36990             k = e.TAB;
36991         }
36992         
36993         if(k == e.TAB){
36994             if(e.shiftKey){
36995                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36996             }else{
36997                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36998                 forward = true;
36999             }
37000             
37001             e.stopEvent();
37002             
37003         }else if(k == e.ENTER &&  !e.ctrlKey){
37004             ed.completeEdit();
37005             e.stopEvent();
37006             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
37007         }else if(k == e.ESC){
37008             ed.cancelEdit();
37009         }
37010         
37011         
37012         if(newCell){
37013             //Roo.log('next cell after edit');
37014             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
37015         } else if (forward) {
37016             // tabbed past last
37017             this.fireEvent.defer(100, this, ['tabend',this]);
37018         }
37019     }
37020 });/*
37021  * Based on:
37022  * Ext JS Library 1.1.1
37023  * Copyright(c) 2006-2007, Ext JS, LLC.
37024  *
37025  * Originally Released Under LGPL - original licence link has changed is not relivant.
37026  *
37027  * Fork - LGPL
37028  * <script type="text/javascript">
37029  */
37030  
37031 /**
37032  * @class Roo.grid.EditorGrid
37033  * @extends Roo.grid.Grid
37034  * Class for creating and editable grid.
37035  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
37036  * The container MUST have some type of size defined for the grid to fill. The container will be 
37037  * automatically set to position relative if it isn't already.
37038  * @param {Object} dataSource The data model to bind to
37039  * @param {Object} colModel The column model with info about this grid's columns
37040  */
37041 Roo.grid.EditorGrid = function(container, config){
37042     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
37043     this.getGridEl().addClass("xedit-grid");
37044
37045     if(!this.selModel){
37046         this.selModel = new Roo.grid.CellSelectionModel();
37047     }
37048
37049     this.activeEditor = null;
37050
37051         this.addEvents({
37052             /**
37053              * @event beforeedit
37054              * Fires before cell editing is triggered. The edit event object has the following properties <br />
37055              * <ul style="padding:5px;padding-left:16px;">
37056              * <li>grid - This grid</li>
37057              * <li>record - The record being edited</li>
37058              * <li>field - The field name being edited</li>
37059              * <li>value - The value for the field being edited.</li>
37060              * <li>row - The grid row index</li>
37061              * <li>column - The grid column index</li>
37062              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
37063              * </ul>
37064              * @param {Object} e An edit event (see above for description)
37065              */
37066             "beforeedit" : true,
37067             /**
37068              * @event afteredit
37069              * Fires after a cell is edited. <br />
37070              * <ul style="padding:5px;padding-left:16px;">
37071              * <li>grid - This grid</li>
37072              * <li>record - The record being edited</li>
37073              * <li>field - The field name being edited</li>
37074              * <li>value - The value being set</li>
37075              * <li>originalValue - The original value for the field, before the edit.</li>
37076              * <li>row - The grid row index</li>
37077              * <li>column - The grid column index</li>
37078              * </ul>
37079              * @param {Object} e An edit event (see above for description)
37080              */
37081             "afteredit" : true,
37082             /**
37083              * @event validateedit
37084              * Fires after a cell is edited, but before the value is set in the record. 
37085          * You can use this to modify the value being set in the field, Return false
37086              * to cancel the change. The edit event object has the following properties <br />
37087              * <ul style="padding:5px;padding-left:16px;">
37088          * <li>editor - This editor</li>
37089              * <li>grid - This grid</li>
37090              * <li>record - The record being edited</li>
37091              * <li>field - The field name being edited</li>
37092              * <li>value - The value being set</li>
37093              * <li>originalValue - The original value for the field, before the edit.</li>
37094              * <li>row - The grid row index</li>
37095              * <li>column - The grid column index</li>
37096              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
37097              * </ul>
37098              * @param {Object} e An edit event (see above for description)
37099              */
37100             "validateedit" : true
37101         });
37102     this.on("bodyscroll", this.stopEditing,  this);
37103     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
37104 };
37105
37106 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
37107     /**
37108      * @cfg {Number} clicksToEdit
37109      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
37110      */
37111     clicksToEdit: 2,
37112
37113     // private
37114     isEditor : true,
37115     // private
37116     trackMouseOver: false, // causes very odd FF errors
37117
37118     onCellDblClick : function(g, row, col){
37119         this.startEditing(row, col);
37120     },
37121
37122     onEditComplete : function(ed, value, startValue){
37123         this.editing = false;
37124         this.activeEditor = null;
37125         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
37126         var r = ed.record;
37127         var field = this.colModel.getDataIndex(ed.col);
37128         var e = {
37129             grid: this,
37130             record: r,
37131             field: field,
37132             originalValue: startValue,
37133             value: value,
37134             row: ed.row,
37135             column: ed.col,
37136             cancel:false,
37137             editor: ed
37138         };
37139         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
37140         cell.show();
37141           
37142         if(String(value) !== String(startValue)){
37143             
37144             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
37145                 r.set(field, e.value);
37146                 // if we are dealing with a combo box..
37147                 // then we also set the 'name' colum to be the displayField
37148                 if (ed.field.displayField && ed.field.name) {
37149                     r.set(ed.field.name, ed.field.el.dom.value);
37150                 }
37151                 
37152                 delete e.cancel; //?? why!!!
37153                 this.fireEvent("afteredit", e);
37154             }
37155         } else {
37156             this.fireEvent("afteredit", e); // always fire it!
37157         }
37158         this.view.focusCell(ed.row, ed.col);
37159     },
37160
37161     /**
37162      * Starts editing the specified for the specified row/column
37163      * @param {Number} rowIndex
37164      * @param {Number} colIndex
37165      */
37166     startEditing : function(row, col){
37167         this.stopEditing();
37168         if(this.colModel.isCellEditable(col, row)){
37169             this.view.ensureVisible(row, col, true);
37170           
37171             var r = this.dataSource.getAt(row);
37172             var field = this.colModel.getDataIndex(col);
37173             var cell = Roo.get(this.view.getCell(row,col));
37174             var e = {
37175                 grid: this,
37176                 record: r,
37177                 field: field,
37178                 value: r.data[field],
37179                 row: row,
37180                 column: col,
37181                 cancel:false 
37182             };
37183             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
37184                 this.editing = true;
37185                 var ed = this.colModel.getCellEditor(col, row);
37186                 
37187                 if (!ed) {
37188                     return;
37189                 }
37190                 if(!ed.rendered){
37191                     ed.render(ed.parentEl || document.body);
37192                 }
37193                 ed.field.reset();
37194                
37195                 cell.hide();
37196                 
37197                 (function(){ // complex but required for focus issues in safari, ie and opera
37198                     ed.row = row;
37199                     ed.col = col;
37200                     ed.record = r;
37201                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
37202                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
37203                     this.activeEditor = ed;
37204                     var v = r.data[field];
37205                     ed.startEdit(this.view.getCell(row, col), v);
37206                     // combo's with 'displayField and name set
37207                     if (ed.field.displayField && ed.field.name) {
37208                         ed.field.el.dom.value = r.data[ed.field.name];
37209                     }
37210                     
37211                     
37212                 }).defer(50, this);
37213             }
37214         }
37215     },
37216         
37217     /**
37218      * Stops any active editing
37219      */
37220     stopEditing : function(){
37221         if(this.activeEditor){
37222             this.activeEditor.completeEdit();
37223         }
37224         this.activeEditor = null;
37225     }
37226 });/*
37227  * Based on:
37228  * Ext JS Library 1.1.1
37229  * Copyright(c) 2006-2007, Ext JS, LLC.
37230  *
37231  * Originally Released Under LGPL - original licence link has changed is not relivant.
37232  *
37233  * Fork - LGPL
37234  * <script type="text/javascript">
37235  */
37236
37237 // private - not really -- you end up using it !
37238 // This is a support class used internally by the Grid components
37239
37240 /**
37241  * @class Roo.grid.GridEditor
37242  * @extends Roo.Editor
37243  * Class for creating and editable grid elements.
37244  * @param {Object} config any settings (must include field)
37245  */
37246 Roo.grid.GridEditor = function(field, config){
37247     if (!config && field.field) {
37248         config = field;
37249         field = Roo.factory(config.field, Roo.form);
37250     }
37251     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
37252     field.monitorTab = false;
37253 };
37254
37255 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
37256     
37257     /**
37258      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
37259      */
37260     
37261     alignment: "tl-tl",
37262     autoSize: "width",
37263     hideEl : false,
37264     cls: "x-small-editor x-grid-editor",
37265     shim:false,
37266     shadow:"frame"
37267 });/*
37268  * Based on:
37269  * Ext JS Library 1.1.1
37270  * Copyright(c) 2006-2007, Ext JS, LLC.
37271  *
37272  * Originally Released Under LGPL - original licence link has changed is not relivant.
37273  *
37274  * Fork - LGPL
37275  * <script type="text/javascript">
37276  */
37277   
37278
37279   
37280 Roo.grid.PropertyRecord = Roo.data.Record.create([
37281     {name:'name',type:'string'},  'value'
37282 ]);
37283
37284
37285 Roo.grid.PropertyStore = function(grid, source){
37286     this.grid = grid;
37287     this.store = new Roo.data.Store({
37288         recordType : Roo.grid.PropertyRecord
37289     });
37290     this.store.on('update', this.onUpdate,  this);
37291     if(source){
37292         this.setSource(source);
37293     }
37294     Roo.grid.PropertyStore.superclass.constructor.call(this);
37295 };
37296
37297
37298
37299 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
37300     setSource : function(o){
37301         this.source = o;
37302         this.store.removeAll();
37303         var data = [];
37304         for(var k in o){
37305             if(this.isEditableValue(o[k])){
37306                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
37307             }
37308         }
37309         this.store.loadRecords({records: data}, {}, true);
37310     },
37311
37312     onUpdate : function(ds, record, type){
37313         if(type == Roo.data.Record.EDIT){
37314             var v = record.data['value'];
37315             var oldValue = record.modified['value'];
37316             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
37317                 this.source[record.id] = v;
37318                 record.commit();
37319                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
37320             }else{
37321                 record.reject();
37322             }
37323         }
37324     },
37325
37326     getProperty : function(row){
37327        return this.store.getAt(row);
37328     },
37329
37330     isEditableValue: function(val){
37331         if(val && val instanceof Date){
37332             return true;
37333         }else if(typeof val == 'object' || typeof val == 'function'){
37334             return false;
37335         }
37336         return true;
37337     },
37338
37339     setValue : function(prop, value){
37340         this.source[prop] = value;
37341         this.store.getById(prop).set('value', value);
37342     },
37343
37344     getSource : function(){
37345         return this.source;
37346     }
37347 });
37348
37349 Roo.grid.PropertyColumnModel = function(grid, store){
37350     this.grid = grid;
37351     var g = Roo.grid;
37352     g.PropertyColumnModel.superclass.constructor.call(this, [
37353         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
37354         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
37355     ]);
37356     this.store = store;
37357     this.bselect = Roo.DomHelper.append(document.body, {
37358         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
37359             {tag: 'option', value: 'true', html: 'true'},
37360             {tag: 'option', value: 'false', html: 'false'}
37361         ]
37362     });
37363     Roo.id(this.bselect);
37364     var f = Roo.form;
37365     this.editors = {
37366         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
37367         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
37368         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
37369         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
37370         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
37371     };
37372     this.renderCellDelegate = this.renderCell.createDelegate(this);
37373     this.renderPropDelegate = this.renderProp.createDelegate(this);
37374 };
37375
37376 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
37377     
37378     
37379     nameText : 'Name',
37380     valueText : 'Value',
37381     
37382     dateFormat : 'm/j/Y',
37383     
37384     
37385     renderDate : function(dateVal){
37386         return dateVal.dateFormat(this.dateFormat);
37387     },
37388
37389     renderBool : function(bVal){
37390         return bVal ? 'true' : 'false';
37391     },
37392
37393     isCellEditable : function(colIndex, rowIndex){
37394         return colIndex == 1;
37395     },
37396
37397     getRenderer : function(col){
37398         return col == 1 ?
37399             this.renderCellDelegate : this.renderPropDelegate;
37400     },
37401
37402     renderProp : function(v){
37403         return this.getPropertyName(v);
37404     },
37405
37406     renderCell : function(val){
37407         var rv = val;
37408         if(val instanceof Date){
37409             rv = this.renderDate(val);
37410         }else if(typeof val == 'boolean'){
37411             rv = this.renderBool(val);
37412         }
37413         return Roo.util.Format.htmlEncode(rv);
37414     },
37415
37416     getPropertyName : function(name){
37417         var pn = this.grid.propertyNames;
37418         return pn && pn[name] ? pn[name] : name;
37419     },
37420
37421     getCellEditor : function(colIndex, rowIndex){
37422         var p = this.store.getProperty(rowIndex);
37423         var n = p.data['name'], val = p.data['value'];
37424         
37425         if(typeof(this.grid.customEditors[n]) == 'string'){
37426             return this.editors[this.grid.customEditors[n]];
37427         }
37428         if(typeof(this.grid.customEditors[n]) != 'undefined'){
37429             return this.grid.customEditors[n];
37430         }
37431         if(val instanceof Date){
37432             return this.editors['date'];
37433         }else if(typeof val == 'number'){
37434             return this.editors['number'];
37435         }else if(typeof val == 'boolean'){
37436             return this.editors['boolean'];
37437         }else{
37438             return this.editors['string'];
37439         }
37440     }
37441 });
37442
37443 /**
37444  * @class Roo.grid.PropertyGrid
37445  * @extends Roo.grid.EditorGrid
37446  * This class represents the  interface of a component based property grid control.
37447  * <br><br>Usage:<pre><code>
37448  var grid = new Roo.grid.PropertyGrid("my-container-id", {
37449       
37450  });
37451  // set any options
37452  grid.render();
37453  * </code></pre>
37454   
37455  * @constructor
37456  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37457  * The container MUST have some type of size defined for the grid to fill. The container will be
37458  * automatically set to position relative if it isn't already.
37459  * @param {Object} config A config object that sets properties on this grid.
37460  */
37461 Roo.grid.PropertyGrid = function(container, config){
37462     config = config || {};
37463     var store = new Roo.grid.PropertyStore(this);
37464     this.store = store;
37465     var cm = new Roo.grid.PropertyColumnModel(this, store);
37466     store.store.sort('name', 'ASC');
37467     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
37468         ds: store.store,
37469         cm: cm,
37470         enableColLock:false,
37471         enableColumnMove:false,
37472         stripeRows:false,
37473         trackMouseOver: false,
37474         clicksToEdit:1
37475     }, config));
37476     this.getGridEl().addClass('x-props-grid');
37477     this.lastEditRow = null;
37478     this.on('columnresize', this.onColumnResize, this);
37479     this.addEvents({
37480          /**
37481              * @event beforepropertychange
37482              * Fires before a property changes (return false to stop?)
37483              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37484              * @param {String} id Record Id
37485              * @param {String} newval New Value
37486          * @param {String} oldval Old Value
37487              */
37488         "beforepropertychange": true,
37489         /**
37490              * @event propertychange
37491              * Fires after a property changes
37492              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37493              * @param {String} id Record Id
37494              * @param {String} newval New Value
37495          * @param {String} oldval Old Value
37496              */
37497         "propertychange": true
37498     });
37499     this.customEditors = this.customEditors || {};
37500 };
37501 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
37502     
37503      /**
37504      * @cfg {Object} customEditors map of colnames=> custom editors.
37505      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
37506      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
37507      * false disables editing of the field.
37508          */
37509     
37510       /**
37511      * @cfg {Object} propertyNames map of property Names to their displayed value
37512          */
37513     
37514     render : function(){
37515         Roo.grid.PropertyGrid.superclass.render.call(this);
37516         this.autoSize.defer(100, this);
37517     },
37518
37519     autoSize : function(){
37520         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
37521         if(this.view){
37522             this.view.fitColumns();
37523         }
37524     },
37525
37526     onColumnResize : function(){
37527         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
37528         this.autoSize();
37529     },
37530     /**
37531      * Sets the data for the Grid
37532      * accepts a Key => Value object of all the elements avaiable.
37533      * @param {Object} data  to appear in grid.
37534      */
37535     setSource : function(source){
37536         this.store.setSource(source);
37537         //this.autoSize();
37538     },
37539     /**
37540      * Gets all the data from the grid.
37541      * @return {Object} data  data stored in grid
37542      */
37543     getSource : function(){
37544         return this.store.getSource();
37545     }
37546 });/*
37547  * Based on:
37548  * Ext JS Library 1.1.1
37549  * Copyright(c) 2006-2007, Ext JS, LLC.
37550  *
37551  * Originally Released Under LGPL - original licence link has changed is not relivant.
37552  *
37553  * Fork - LGPL
37554  * <script type="text/javascript">
37555  */
37556  
37557 /**
37558  * @class Roo.LoadMask
37559  * A simple utility class for generically masking elements while loading data.  If the element being masked has
37560  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
37561  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
37562  * element's UpdateManager load indicator and will be destroyed after the initial load.
37563  * @constructor
37564  * Create a new LoadMask
37565  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
37566  * @param {Object} config The config object
37567  */
37568 Roo.LoadMask = function(el, config){
37569     this.el = Roo.get(el);
37570     Roo.apply(this, config);
37571     if(this.store){
37572         this.store.on('beforeload', this.onBeforeLoad, this);
37573         this.store.on('load', this.onLoad, this);
37574         this.store.on('loadexception', this.onLoadException, this);
37575         this.removeMask = false;
37576     }else{
37577         var um = this.el.getUpdateManager();
37578         um.showLoadIndicator = false; // disable the default indicator
37579         um.on('beforeupdate', this.onBeforeLoad, this);
37580         um.on('update', this.onLoad, this);
37581         um.on('failure', this.onLoad, this);
37582         this.removeMask = true;
37583     }
37584 };
37585
37586 Roo.LoadMask.prototype = {
37587     /**
37588      * @cfg {Boolean} removeMask
37589      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
37590      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
37591      */
37592     /**
37593      * @cfg {String} msg
37594      * The text to display in a centered loading message box (defaults to 'Loading...')
37595      */
37596     msg : 'Loading...',
37597     /**
37598      * @cfg {String} msgCls
37599      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
37600      */
37601     msgCls : 'x-mask-loading',
37602
37603     /**
37604      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
37605      * @type Boolean
37606      */
37607     disabled: false,
37608
37609     /**
37610      * Disables the mask to prevent it from being displayed
37611      */
37612     disable : function(){
37613        this.disabled = true;
37614     },
37615
37616     /**
37617      * Enables the mask so that it can be displayed
37618      */
37619     enable : function(){
37620         this.disabled = false;
37621     },
37622     
37623     onLoadException : function()
37624     {
37625         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
37626             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
37627         }
37628         this.el.unmask(this.removeMask);
37629     },
37630     // private
37631     onLoad : function()
37632     {
37633         this.el.unmask(this.removeMask);
37634     },
37635
37636     // private
37637     onBeforeLoad : function(){
37638         if(!this.disabled){
37639             this.el.mask(this.msg, this.msgCls);
37640         }
37641     },
37642
37643     // private
37644     destroy : function(){
37645         if(this.store){
37646             this.store.un('beforeload', this.onBeforeLoad, this);
37647             this.store.un('load', this.onLoad, this);
37648             this.store.un('loadexception', this.onLoadException, this);
37649         }else{
37650             var um = this.el.getUpdateManager();
37651             um.un('beforeupdate', this.onBeforeLoad, this);
37652             um.un('update', this.onLoad, this);
37653             um.un('failure', this.onLoad, this);
37654         }
37655     }
37656 };/*
37657  * Based on:
37658  * Ext JS Library 1.1.1
37659  * Copyright(c) 2006-2007, Ext JS, LLC.
37660  *
37661  * Originally Released Under LGPL - original licence link has changed is not relivant.
37662  *
37663  * Fork - LGPL
37664  * <script type="text/javascript">
37665  */
37666 Roo.XTemplate = function(){
37667     Roo.XTemplate.superclass.constructor.apply(this, arguments);
37668     var s = this.html;
37669
37670     s = ['<tpl>', s, '</tpl>'].join('');
37671
37672     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
37673
37674     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
37675     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
37676     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
37677     var m, id = 0;
37678     var tpls = [];
37679
37680     while(m = s.match(re)){
37681        var m2 = m[0].match(nameRe);
37682        var m3 = m[0].match(ifRe);
37683        var m4 = m[0].match(execRe);
37684        var exp = null, fn = null, exec = null;
37685        var name = m2 && m2[1] ? m2[1] : '';
37686        if(m3){
37687            exp = m3 && m3[1] ? m3[1] : null;
37688            if(exp){
37689                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
37690            }
37691        }
37692        if(m4){
37693            exp = m4 && m4[1] ? m4[1] : null;
37694            if(exp){
37695                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
37696            }
37697        }
37698        if(name){
37699            switch(name){
37700                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
37701                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
37702                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
37703            }
37704        }
37705        tpls.push({
37706             id: id,
37707             target: name,
37708             exec: exec,
37709             test: fn,
37710             body: m[1]||''
37711         });
37712        s = s.replace(m[0], '{xtpl'+ id + '}');
37713        ++id;
37714     }
37715     for(var i = tpls.length-1; i >= 0; --i){
37716         this.compileTpl(tpls[i]);
37717     }
37718     this.master = tpls[tpls.length-1];
37719     this.tpls = tpls;
37720 };
37721 Roo.extend(Roo.XTemplate, Roo.Template, {
37722
37723     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
37724
37725     applySubTemplate : function(id, values, parent){
37726         var t = this.tpls[id];
37727         if(t.test && !t.test.call(this, values, parent)){
37728             return '';
37729         }
37730         if(t.exec && t.exec.call(this, values, parent)){
37731             return '';
37732         }
37733         var vs = t.target ? t.target.call(this, values, parent) : values;
37734         parent = t.target ? values : parent;
37735         if(t.target && vs instanceof Array){
37736             var buf = [];
37737             for(var i = 0, len = vs.length; i < len; i++){
37738                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
37739             }
37740             return buf.join('');
37741         }
37742         return t.compiled.call(this, vs, parent);
37743     },
37744
37745     compileTpl : function(tpl){
37746         var fm = Roo.util.Format;
37747         var useF = this.disableFormats !== true;
37748         var sep = Roo.isGecko ? "+" : ",";
37749         var fn = function(m, name, format, args){
37750             if(name.substr(0, 4) == 'xtpl'){
37751                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
37752             }
37753             var v;
37754             if(name.indexOf('.') != -1){
37755                 v = name;
37756             }else{
37757                 v = "values['" + name + "']";
37758             }
37759             if(format && useF){
37760                 args = args ? ',' + args : "";
37761                 if(format.substr(0, 5) != "this."){
37762                     format = "fm." + format + '(';
37763                 }else{
37764                     format = 'this.call("'+ format.substr(5) + '", ';
37765                     args = ", values";
37766                 }
37767             }else{
37768                 args= ''; format = "("+v+" === undefined ? '' : ";
37769             }
37770             return "'"+ sep + format + v + args + ")"+sep+"'";
37771         };
37772         var body;
37773         // branched to use + in gecko and [].join() in others
37774         if(Roo.isGecko){
37775             body = "tpl.compiled = function(values, parent){ return '" +
37776                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
37777                     "';};";
37778         }else{
37779             body = ["tpl.compiled = function(values, parent){ return ['"];
37780             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
37781             body.push("'].join('');};");
37782             body = body.join('');
37783         }
37784         /** eval:var:zzzzzzz */
37785         eval(body);
37786         return this;
37787     },
37788
37789     applyTemplate : function(values){
37790         return this.master.compiled.call(this, values, {});
37791         var s = this.subs;
37792     },
37793
37794     apply : function(){
37795         return this.applyTemplate.apply(this, arguments);
37796     },
37797
37798     compile : function(){return this;}
37799 });
37800
37801 Roo.XTemplate.from = function(el){
37802     el = Roo.getDom(el);
37803     return new Roo.XTemplate(el.value || el.innerHTML);
37804 };/*
37805  * Original code for Roojs - LGPL
37806  * <script type="text/javascript">
37807  */
37808  
37809 /**
37810  * @class Roo.XComponent
37811  * A delayed Element creator...
37812  * Or a way to group chunks of interface together.
37813  * 
37814  * Mypart.xyx = new Roo.XComponent({
37815
37816     parent : 'Mypart.xyz', // empty == document.element.!!
37817     order : '001',
37818     name : 'xxxx'
37819     region : 'xxxx'
37820     disabled : function() {} 
37821      
37822     tree : function() { // return an tree of xtype declared components
37823         var MODULE = this;
37824         return 
37825         {
37826             xtype : 'NestedLayoutPanel',
37827             // technicall
37828         }
37829      ]
37830  *})
37831  *
37832  *
37833  * It can be used to build a big heiracy, with parent etc.
37834  * or you can just use this to render a single compoent to a dom element
37835  * MYPART.render(Roo.Element | String(id) | dom_element )
37836  * 
37837  * @extends Roo.util.Observable
37838  * @constructor
37839  * @param cfg {Object} configuration of component
37840  * 
37841  */
37842 Roo.XComponent = function(cfg) {
37843     Roo.apply(this, cfg);
37844     this.addEvents({ 
37845         /**
37846              * @event built
37847              * Fires when this the componnt is built
37848              * @param {Roo.XComponent} c the component
37849              */
37850         'built' : true,
37851         /**
37852              * @event buildcomplete
37853              * Fires on the top level element when all elements have been built
37854              * @param {Roo.XComponent} c the top level component.
37855          */
37856         'buildcomplete' : true
37857         
37858     });
37859     this.region = this.region || 'center'; // default..
37860     Roo.XComponent.register(this);
37861     this.modules = false;
37862     this.el = false; // where the layout goes..
37863     
37864     
37865 }
37866 Roo.extend(Roo.XComponent, Roo.util.Observable, {
37867     /**
37868      * @property el
37869      * The created element (with Roo.factory())
37870      * @type {Roo.Layout}
37871      */
37872     el  : false,
37873     
37874     /**
37875      * @property el
37876      * for BC  - use el in new code
37877      * @type {Roo.Layout}
37878      */
37879     panel : false,
37880     
37881     /**
37882      * @property layout
37883      * for BC  - use el in new code
37884      * @type {Roo.Layout}
37885      */
37886     layout : false,
37887     
37888      /**
37889      * @cfg {Function|boolean} disabled
37890      * If this module is disabled by some rule, return true from the funtion
37891      */
37892     disabled : false,
37893     
37894     /**
37895      * @cfg {String} parent 
37896      * Name of parent element which it get xtype added to..
37897      */
37898     parent: false,
37899     
37900     /**
37901      * @cfg {String} order
37902      * Used to set the order in which elements are created (usefull for multiple tabs)
37903      */
37904     
37905     order : false,
37906     /**
37907      * @cfg {String} name
37908      * String to display while loading.
37909      */
37910     name : false,
37911     /**
37912      * @cfg {String} region
37913      * Region to render component to (defaults to center)
37914      */
37915     region : 'center',
37916     
37917     /**
37918      * @cfg {Array} items
37919      * A single item array - the first element is the root of the tree..
37920      * It's done this way to stay compatible with the Xtype system...
37921      */
37922     items : false,
37923     
37924     
37925      /**
37926      * render
37927      * render element to dom or tree
37928      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
37929      */
37930     
37931     render : function(el)
37932     {
37933         
37934         el = el || false;
37935         var hp = this.parent ? 1 : 0;
37936         
37937         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
37938             // if parent is a '#.....' string, then let's use that..
37939             var ename = this.parent.substr(1)
37940             this.parent = false;
37941             el = Roo.get(ename);
37942             if (!el) {
37943                 Roo.log("Warning - element can not be found :#" + ename );
37944                 return;
37945             }
37946         }
37947         
37948         
37949         if (!this.parent) {
37950             
37951             el = el ? Roo.get(el) : false;
37952             
37953             // it's a top level one..
37954             this.parent =  {
37955                 el : new Roo.BorderLayout(el || document.body, {
37956                 
37957                      center: {
37958                          titlebar: false,
37959                          autoScroll:false,
37960                          closeOnTab: true,
37961                          tabPosition: 'top',
37962                           //resizeTabs: true,
37963                          alwaysShowTabs: el && hp? false :  true,
37964                          hideTabs: el || !hp ? true :  false,
37965                          minTabWidth: 140
37966                      }
37967                  })
37968             }
37969         }
37970         
37971         
37972             
37973         var tree = this.tree();
37974         tree.region = tree.region || this.region;
37975         this.el = this.parent.el.addxtype(tree);
37976         this.fireEvent('built', this);
37977         
37978         this.panel = this.el;
37979         this.layout = this.panel.layout;    
37980          
37981     }
37982     
37983 });
37984
37985 Roo.apply(Roo.XComponent, {
37986     
37987     /**
37988      * @property  buildCompleted
37989      * True when the builder has completed building the interface.
37990      * @type Boolean
37991      */
37992     buildCompleted : false,
37993      
37994     /**
37995      * @property  topModule
37996      * the upper most module - uses document.element as it's constructor.
37997      * @type Object
37998      */
37999      
38000     topModule  : false,
38001       
38002     /**
38003      * @property  modules
38004      * array of modules to be created by registration system.
38005      * @type {Array} of Roo.XComponent
38006      */
38007     
38008     modules : [],
38009     /**
38010      * @property  elmodules
38011      * array of modules to be created by which use #ID 
38012      * @type {Array} of Roo.XComponent
38013      */
38014      
38015     elmodules : [],
38016
38017     
38018     /**
38019      * Register components to be built later.
38020      *
38021      * This solves the following issues
38022      * - Building is not done on page load, but after an authentication process has occured.
38023      * - Interface elements are registered on page load
38024      * - Parent Interface elements may not be loaded before child, so this handles that..
38025      * 
38026      *
38027      * example:
38028      * 
38029      * MyApp.register({
38030           order : '000001',
38031           module : 'Pman.Tab.projectMgr',
38032           region : 'center',
38033           parent : 'Pman.layout',
38034           disabled : false,  // or use a function..
38035         })
38036      
38037      * * @param {Object} details about module
38038      */
38039     register : function(obj) {
38040         this.modules.push(obj);
38041          
38042     },
38043     /**
38044      * convert a string to an object..
38045      * eg. 'AAA.BBB' -> finds AAA.BBB
38046
38047      */
38048     
38049     toObject : function(str)
38050     {
38051         if (!str || typeof(str) == 'object') {
38052             return str;
38053         }
38054         if (str.substring(0,1) == '#') {
38055             return str;
38056         }
38057
38058         var ar = str.split('.');
38059         var rt, o;
38060         rt = ar.shift();
38061             /** eval:var:o */
38062         try {
38063             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
38064         } catch (e) {
38065             throw "Module not found : " + str;
38066         }
38067         
38068         if (o === false) {
38069             throw "Module not found : " + str;
38070         }
38071         Roo.each(ar, function(e) {
38072             if (typeof(o[e]) == 'undefined') {
38073                 throw "Module not found : " + str;
38074             }
38075             o = o[e];
38076         });
38077         
38078         return o;
38079         
38080     },
38081     
38082     
38083     /**
38084      * move modules into their correct place in the tree..
38085      * 
38086      */
38087     preBuild : function ()
38088     {
38089         var _t = this;
38090         Roo.each(this.modules , function (obj)
38091         {
38092             var opar = obj.parent;
38093             try { 
38094                 obj.parent = this.toObject(opar);
38095             } catch(e) {
38096                 Roo.log(e.toString());
38097                 return;
38098             }
38099             
38100             if (!obj.parent) {
38101                 this.topModule = obj;
38102                 return;
38103             }
38104             if (typeof(obj.parent) == 'string') {
38105                 this.elmodules.push(obj);
38106                 return;
38107             }
38108             if (obj.parent.constructor != Roo.XComponent) {
38109                 Roo.log("Object Parent is not instance of XComponent:" + obj.name)
38110             }
38111             if (!obj.parent.modules) {
38112                 obj.parent.modules = new Roo.util.MixedCollection(false, 
38113                     function(o) { return o.order + '' }
38114                 );
38115             }
38116             
38117             obj.parent.modules.add(obj);
38118         }, this);
38119     },
38120     
38121      /**
38122      * make a list of modules to build.
38123      * @return {Array} list of modules. 
38124      */ 
38125     
38126     buildOrder : function()
38127     {
38128         var _this = this;
38129         var cmp = function(a,b) {   
38130             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
38131         };
38132         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
38133             throw "No top level modules to build";
38134         }
38135         
38136         // make a flat list in order of modules to build.
38137         var mods = this.topModule ? [ this.topModule ] : [];
38138         Roo.each(this.elmodules,function(e) { mods.push(e) });
38139
38140         
38141         // add modules to their parents..
38142         var addMod = function(m) {
38143            // Roo.debug && Roo.log(m.modKey);
38144             
38145             mods.push(m);
38146             if (m.modules) {
38147                 m.modules.keySort('ASC',  cmp );
38148                 m.modules.each(addMod);
38149             }
38150             // not sure if this is used any more..
38151             if (m.finalize) {
38152                 m.finalize.name = m.name + " (clean up) ";
38153                 mods.push(m.finalize);
38154             }
38155             
38156         }
38157         if (this.topModule) { 
38158             this.topModule.modules.keySort('ASC',  cmp );
38159             this.topModule.modules.each(addMod);
38160         }
38161         return mods;
38162     },
38163     
38164      /**
38165      * Build the registered modules.
38166      * @param {Object} parent element.
38167      * @param {Function} optional method to call after module has been added.
38168      * 
38169      */ 
38170    
38171     build : function() 
38172     {
38173         
38174         this.preBuild();
38175         var mods = this.buildOrder();
38176       
38177         //this.allmods = mods;
38178         //Roo.debug && Roo.log(mods);
38179         //return;
38180         if (!mods.length) { // should not happen
38181             throw "NO modules!!!";
38182         }
38183         
38184         
38185         
38186         // flash it up as modal - so we store the mask!?
38187         Roo.MessageBox.show({ title: 'loading' });
38188         Roo.MessageBox.show({
38189            title: "Please wait...",
38190            msg: "Building Interface...",
38191            width:450,
38192            progress:true,
38193            closable:false,
38194            modal: false
38195           
38196         });
38197         var total = mods.length;
38198         
38199         var _this = this;
38200         var progressRun = function() {
38201             if (!mods.length) {
38202                 Roo.debug && Roo.log('hide?');
38203                 Roo.MessageBox.hide();
38204                 if (_this.topModule) { 
38205                     _this.topModule.fireEvent('buildcomplete', _this.topModule);
38206                 }
38207                 // THE END...
38208                 return false;   
38209             }
38210             
38211             var m = mods.shift();
38212             
38213             
38214             Roo.debug && Roo.log(m);
38215             // not sure if this is supported any more.. - modules that are are just function
38216             if (typeof(m) == 'function') { 
38217                 m.call(this);
38218                 return progressRun.defer(10, _this);
38219             } 
38220             
38221             
38222             
38223             Roo.MessageBox.updateProgress(
38224                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
38225                     " of " + total + 
38226                     (m.name ? (' - ' + m.name) : '')
38227                     );
38228             
38229          
38230             // is the module disabled?
38231             var disabled = (typeof(m.disabled) == 'function') ?
38232                 m.disabled.call(m.module.disabled) : m.disabled;    
38233             
38234             
38235             if (disabled) {
38236                 return progressRun(); // we do not update the display!
38237             }
38238             
38239             // now build 
38240             
38241             m.render();
38242             // it's 10 on top level, and 1 on others??? why...
38243             return progressRun.defer(10, _this);
38244              
38245         }
38246         progressRun.defer(1, _this);
38247      
38248         
38249         
38250     }
38251     
38252      
38253    
38254     
38255     
38256 });
38257  //<script type="text/javascript">
38258
38259
38260 /**
38261  * @class Roo.Login
38262  * @extends Roo.LayoutDialog
38263  * A generic Login Dialog..... - only one needed in theory!?!?
38264  *
38265  * Fires XComponent builder on success...
38266  * 
38267  * Sends 
38268  *    username,password, lang = for login actions.
38269  *    check = 1 for periodic checking that sesion is valid.
38270  *    passwordRequest = email request password
38271  *    logout = 1 = to logout
38272  * 
38273  * Affects: (this id="????" elements)
38274  *   loading  (removed) (used to indicate application is loading)
38275  *   loading-mask (hides) (used to hide application when it's building loading)
38276  *   
38277  * 
38278  * Usage: 
38279  *    
38280  * 
38281  * Myapp.login = Roo.Login({
38282      url: xxxx,
38283    
38284      realm : 'Myapp', 
38285      
38286      
38287      method : 'POST',
38288      
38289      
38290      * 
38291  })
38292  * 
38293  * 
38294  * 
38295  **/
38296  
38297 Roo.Login = function(cfg)
38298 {
38299     this.addEvents({
38300         'refreshed' : true
38301     });
38302     
38303     Roo.apply(this,cfg);
38304     
38305     Roo.onReady(function() {
38306         this.onLoad();
38307     }, this);
38308     // call parent..
38309     
38310    
38311     Roo.Login.superclass.constructor.call(this, this);
38312     //this.addxtype(this.items[0]);
38313     
38314     
38315 }
38316
38317
38318 Roo.extend(Roo.Login, Roo.LayoutDialog, {
38319     
38320     /**
38321      * @cfg {String} method
38322      * Method used to query for login details.
38323      */
38324     
38325     method : 'POST',
38326     /**
38327      * @cfg {String} url
38328      * URL to query login data. - eg. baseURL + '/Login.php'
38329      */
38330     url : '',
38331     
38332     /**
38333      * @property user
38334      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
38335      * @type {Object} 
38336      */
38337     user : false,
38338     /**
38339      * @property checkFails
38340      * Number of times we have attempted to get authentication check, and failed.
38341      * @type {Number} 
38342      */
38343     checkFails : 0,
38344       /**
38345      * @property intervalID
38346      * The window interval that does the constant login checking.
38347      * @type {Number} 
38348      */
38349     intervalID : 0,
38350     
38351     
38352     onLoad : function() // called on page load...
38353     {
38354         // load 
38355          
38356         if (Roo.get('loading')) { // clear any loading indicator..
38357             Roo.get('loading').remove();
38358         }
38359         
38360         //this.switchLang('en'); // set the language to english..
38361        
38362         this.check({
38363             success:  function(response, opts)  {  // check successfull...
38364             
38365                 var res = this.processResponse(response);
38366                 this.checkFails =0;
38367                 if (!res.success) { // error!
38368                     this.checkFails = 5;
38369                     //console.log('call failure');
38370                     return this.failure(response,opts);
38371                 }
38372                 
38373                 if (!res.data.id) { // id=0 == login failure.
38374                     return this.show();
38375                 }
38376                 
38377                               
38378                         //console.log(success);
38379                 this.fillAuth(res.data);   
38380                 this.checkFails =0;
38381                 Roo.XComponent.build();
38382             },
38383             failure : this.show
38384         });
38385         
38386     }, 
38387     
38388     
38389     check: function(cfg) // called every so often to refresh cookie etc..
38390     {
38391         if (cfg.again) { // could be undefined..
38392             this.checkFails++;
38393         } else {
38394             this.checkFails = 0;
38395         }
38396         var _this = this;
38397         if (this.sending) {
38398             if ( this.checkFails > 4) {
38399                 Roo.MessageBox.alert("Error",  
38400                     "Error getting authentication status. - try reloading, or wait a while", function() {
38401                         _this.sending = false;
38402                     }); 
38403                 return;
38404             }
38405             cfg.again = true;
38406             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
38407             return;
38408         }
38409         this.sending = true;
38410         
38411         Roo.Ajax.request({  
38412             url: this.url,
38413             params: {
38414                 getAuthUser: true
38415             },  
38416             method: this.method,
38417             success:  cfg.success || this.success,
38418             failure : cfg.failure || this.failure,
38419             scope : this,
38420             callCfg : cfg
38421               
38422         });  
38423     }, 
38424     
38425     
38426     logout: function()
38427     {
38428         window.onbeforeunload = function() { }; // false does not work for IE..
38429         this.user = false;
38430         var _this = this;
38431         
38432         Roo.Ajax.request({  
38433             url: this.url,
38434             params: {
38435                 logout: 1
38436             },  
38437             method: 'GET',
38438             failure : function() {
38439                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
38440                     document.location = document.location.toString() + '?ts=' + Math.random();
38441                 });
38442                 
38443             },
38444             success : function() {
38445                 _this.user = false;
38446                 this.checkFails =0;
38447                 // fixme..
38448                 document.location = document.location.toString() + '?ts=' + Math.random();
38449             }
38450               
38451               
38452         }); 
38453     },
38454     
38455     processResponse : function (response)
38456     {
38457         var res = '';
38458         try {
38459             res = Roo.decode(response.responseText);
38460             // oops...
38461             if (typeof(res) != 'object') {
38462                 res = { success : false, errorMsg : res, errors : true };
38463             }
38464             if (typeof(res.success) == 'undefined') {
38465                 res.success = false;
38466             }
38467             
38468         } catch(e) {
38469             res = { success : false,  errorMsg : response.responseText, errors : true };
38470         }
38471         return res;
38472     },
38473     
38474     success : function(response, opts)  // check successfull...
38475     {  
38476         this.sending = false;
38477         var res = this.processResponse(response);
38478         if (!res.success) {
38479             return this.failure(response, opts);
38480         }
38481         if (!res.data || !res.data.id) {
38482             return this.failure(response,opts);
38483         }
38484         //console.log(res);
38485         this.fillAuth(res.data);
38486         
38487         this.checkFails =0;
38488         
38489     },
38490     
38491     
38492     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
38493     {
38494         this.authUser = -1;
38495         this.sending = false;
38496         var res = this.processResponse(response);
38497         //console.log(res);
38498         if ( this.checkFails > 2) {
38499         
38500             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
38501                 "Error getting authentication status. - try reloading"); 
38502             return;
38503         }
38504         opts.callCfg.again = true;
38505         this.check.defer(1000, this, [ opts.callCfg ]);
38506         return;  
38507     },
38508     
38509     
38510     
38511     fillAuth: function(au) {
38512         this.startAuthCheck();
38513         this.authUserId = au.id;
38514         this.authUser = au;
38515         this.lastChecked = new Date();
38516         this.fireEvent('refreshed', au);
38517         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
38518         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
38519         au.lang = au.lang || 'en';
38520         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
38521         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
38522         this.switchLang(au.lang );
38523         
38524      
38525         // open system... - -on setyp..
38526         if (this.authUserId  < 0) {
38527             Roo.MessageBox.alert("Warning", 
38528                 "This is an open system - please set up a admin user with a password.");  
38529         }
38530          
38531         //Pman.onload(); // which should do nothing if it's a re-auth result...
38532         
38533              
38534     },
38535     
38536     startAuthCheck : function() // starter for timeout checking..
38537     {
38538         if (this.intervalID) { // timer already in place...
38539             return false;
38540         }
38541         var _this = this;
38542         this.intervalID =  window.setInterval(function() {
38543               _this.check(false);
38544             }, 120000); // every 120 secs = 2mins..
38545         
38546         
38547     },
38548          
38549     
38550     switchLang : function (lang) 
38551     {
38552         _T = typeof(_T) == 'undefined' ? false : _T;
38553           if (!_T || !lang.length) {
38554             return;
38555         }
38556         
38557         if (!_T && lang != 'en') {
38558             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
38559             return;
38560         }
38561         
38562         if (typeof(_T.en) == 'undefined') {
38563             _T.en = {};
38564             Roo.apply(_T.en, _T);
38565         }
38566         
38567         if (typeof(_T[lang]) == 'undefined') {
38568             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
38569             return;
38570         }
38571         
38572         
38573         Roo.apply(_T, _T[lang]);
38574         // just need to set the text values for everything...
38575         var _this = this;
38576         /* this will not work ...
38577         if (this.form) { 
38578             
38579                
38580             function formLabel(name, val) {
38581                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
38582             }
38583             
38584             formLabel('password', "Password"+':');
38585             formLabel('username', "Email Address"+':');
38586             formLabel('lang', "Language"+':');
38587             this.dialog.setTitle("Login");
38588             this.dialog.buttons[0].setText("Forgot Password");
38589             this.dialog.buttons[1].setText("Login");
38590         }
38591         */
38592         
38593         
38594     },
38595     
38596     
38597     title: "Login",
38598     modal: true,
38599     width:  350,
38600     //height: 230,
38601     height: 180,
38602     shadow: true,
38603     minWidth:200,
38604     minHeight:180,
38605     //proxyDrag: true,
38606     closable: false,
38607     draggable: false,
38608     collapsible: false,
38609     resizable: false,
38610     center: {  // needed??
38611         autoScroll:false,
38612         titlebar: false,
38613        // tabPosition: 'top',
38614         hideTabs: true,
38615         closeOnTab: true,
38616         alwaysShowTabs: false
38617     } ,
38618     listeners : {
38619         
38620         show  : function(dlg)
38621         {
38622             //console.log(this);
38623             this.form = this.layout.getRegion('center').activePanel.form;
38624             this.form.dialog = dlg;
38625             this.buttons[0].form = this.form;
38626             this.buttons[0].dialog = dlg;
38627             this.buttons[1].form = this.form;
38628             this.buttons[1].dialog = dlg;
38629            
38630            //this.resizeToLogo.defer(1000,this);
38631             // this is all related to resizing for logos..
38632             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
38633            //// if (!sz) {
38634              //   this.resizeToLogo.defer(1000,this);
38635              //   return;
38636            // }
38637             //var w = Ext.lib.Dom.getViewWidth() - 100;
38638             //var h = Ext.lib.Dom.getViewHeight() - 100;
38639             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
38640             //this.center();
38641             if (this.disabled) {
38642                 this.hide();
38643                 return;
38644             }
38645             
38646             if (this.user.id < 0) { // used for inital setup situations.
38647                 return;
38648             }
38649             
38650             if (this.intervalID) {
38651                 // remove the timer
38652                 window.clearInterval(this.intervalID);
38653                 this.intervalID = false;
38654             }
38655             
38656             
38657             if (Roo.get('loading')) {
38658                 Roo.get('loading').remove();
38659             }
38660             if (Roo.get('loading-mask')) {
38661                 Roo.get('loading-mask').hide();
38662             }
38663             
38664             //incomming._node = tnode;
38665             this.form.reset();
38666             //this.dialog.modal = !modal;
38667             //this.dialog.show();
38668             this.el.unmask(); 
38669             
38670             
38671             this.form.setValues({
38672                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
38673                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
38674             });
38675             
38676             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
38677             if (this.form.findField('username').getValue().length > 0 ){
38678                 this.form.findField('password').focus();
38679             } else {
38680                this.form.findField('username').focus();
38681             }
38682     
38683         }
38684     },
38685     items : [
38686          {
38687        
38688             xtype : 'ContentPanel',
38689             xns : Roo,
38690             region: 'center',
38691             fitToFrame : true,
38692             
38693             items : [
38694     
38695                 {
38696                
38697                     xtype : 'Form',
38698                     xns : Roo.form,
38699                     labelWidth: 100,
38700                     style : 'margin: 10px;',
38701                     
38702                     listeners : {
38703                         actionfailed : function(f, act) {
38704                             // form can return { errors: .... }
38705                                 
38706                             //act.result.errors // invalid form element list...
38707                             //act.result.errorMsg// invalid form element list...
38708                             
38709                             this.dialog.el.unmask();
38710                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
38711                                         "Login failed - communication error - try again.");
38712                                       
38713                         },
38714                         actioncomplete: function(re, act) {
38715                              
38716                             Roo.state.Manager.set(
38717                                 this.dialog.realm + '.username',  
38718                                     this.findField('username').getValue()
38719                             );
38720                             Roo.state.Manager.set(
38721                                 this.dialog.realm + '.lang',  
38722                                 this.findField('lang').getValue() 
38723                             );
38724                             
38725                             this.dialog.fillAuth(act.result.data);
38726                               
38727                             this.dialog.hide();
38728                             
38729                             if (Roo.get('loading-mask')) {
38730                                 Roo.get('loading-mask').show();
38731                             }
38732                             Roo.XComponent.build();
38733                             
38734                              
38735                             
38736                         }
38737                     },
38738                     items : [
38739                         {
38740                             xtype : 'TextField',
38741                             xns : Roo.form,
38742                             fieldLabel: "Email Address",
38743                             name: 'username',
38744                             width:200,
38745                             autoCreate : {tag: "input", type: "text", size: "20"}
38746                         },
38747                         {
38748                             xtype : 'TextField',
38749                             xns : Roo.form,
38750                             fieldLabel: "Password",
38751                             inputType: 'password',
38752                             name: 'password',
38753                             width:200,
38754                             autoCreate : {tag: "input", type: "text", size: "20"},
38755                             listeners : {
38756                                 specialkey : function(e,ev) {
38757                                     if (ev.keyCode == 13) {
38758                                         this.form.dialog.el.mask("Logging in");
38759                                         this.form.doAction('submit', {
38760                                             url: this.form.dialog.url,
38761                                             method: this.form.dialog.method
38762                                         });
38763                                     }
38764                                 }
38765                             }  
38766                         },
38767                         {
38768                             xtype : 'ComboBox',
38769                             xns : Roo.form,
38770                             fieldLabel: "Language",
38771                             name : 'langdisp',
38772                             store: {
38773                                 xtype : 'SimpleStore',
38774                                 fields: ['lang', 'ldisp'],
38775                                 data : [
38776                                     [ 'en', 'English' ],
38777                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
38778                                     [ 'zh_CN', '\u7C21\u4E2D' ]
38779                                 ]
38780                             },
38781                             
38782                             valueField : 'lang',
38783                             hiddenName:  'lang',
38784                             width: 200,
38785                             displayField:'ldisp',
38786                             typeAhead: false,
38787                             editable: false,
38788                             mode: 'local',
38789                             triggerAction: 'all',
38790                             emptyText:'Select a Language...',
38791                             selectOnFocus:true,
38792                             listeners : {
38793                                 select :  function(cb, rec, ix) {
38794                                     this.form.switchLang(rec.data.lang);
38795                                 }
38796                             }
38797                         
38798                         }
38799                     ]
38800                 }
38801                   
38802                 
38803             ]
38804         }
38805     ],
38806     buttons : [
38807         {
38808             xtype : 'Button',
38809             xns : 'Roo',
38810             text : "Forgot Password",
38811             listeners : {
38812                 click : function() {
38813                     //console.log(this);
38814                     var n = this.form.findField('username').getValue();
38815                     if (!n.length) {
38816                         Roo.MessageBox.alert("Error", "Fill in your email address");
38817                         return;
38818                     }
38819                     Roo.Ajax.request({
38820                         url: this.dialog.url,
38821                         params: {
38822                             passwordRequest: n
38823                         },
38824                         method: this.dialog.method,
38825                         success:  function(response, opts)  {  // check successfull...
38826                         
38827                             var res = this.dialog.processResponse(response);
38828                             if (!res.success) { // error!
38829                                Roo.MessageBox.alert("Error" ,
38830                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
38831                                return;
38832                             }
38833                             Roo.MessageBox.alert("Notice" ,
38834                                 "Please check you email for the Password Reset message");
38835                         },
38836                         failure : function() {
38837                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
38838                         }
38839                         
38840                     });
38841                 }
38842             }
38843         },
38844         {
38845             xtype : 'Button',
38846             xns : 'Roo',
38847             text : "Login",
38848             listeners : {
38849                 
38850                 click : function () {
38851                         
38852                     this.dialog.el.mask("Logging in");
38853                     this.form.doAction('submit', {
38854                             url: this.dialog.url,
38855                             method: this.dialog.method
38856                     });
38857                 }
38858             }
38859         }
38860     ]
38861   
38862   
38863 })
38864  
38865
38866
38867