roojs-all.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13
14 /*
15  * These classes are derivatives of the similarly named classes in the YUI Library.
16  * The original license:
17  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18  * Code licensed under the BSD License:
19  * http://developer.yahoo.net/yui/license.txt
20  */
21
22 (function() {
23
24 var Event=Roo.EventManager;
25 var Dom=Roo.lib.Dom;
26
27 /**
28  * @class Roo.dd.DragDrop
29  * @extends Roo.util.Observable
30  * Defines the interface and base operation of items that that can be
31  * dragged or can be drop targets.  It was designed to be extended, overriding
32  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
33  * Up to three html elements can be associated with a DragDrop instance:
34  * <ul>
35  * <li>linked element: the element that is passed into the constructor.
36  * This is the element which defines the boundaries for interaction with
37  * other DragDrop objects.</li>
38  * <li>handle element(s): The drag operation only occurs if the element that
39  * was clicked matches a handle element.  By default this is the linked
40  * element, but there are times that you will want only a portion of the
41  * linked element to initiate the drag operation, and the setHandleElId()
42  * method provides a way to define this.</li>
43  * <li>drag element: this represents the element that would be moved along
44  * with the cursor during a drag operation.  By default, this is the linked
45  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
46  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
47  * </li>
48  * </ul>
49  * This class should not be instantiated until the onload event to ensure that
50  * the associated elements are available.
51  * The following would define a DragDrop obj that would interact with any
52  * other DragDrop obj in the "group1" group:
53  * <pre>
54  *  dd = new Roo.dd.DragDrop("div1", "group1");
55  * </pre>
56  * Since none of the event handlers have been implemented, nothing would
57  * actually happen if you were to run the code above.  Normally you would
58  * override this class or one of the default implementations, but you can
59  * also override the methods you want on an instance of the class...
60  * <pre>
61  *  dd.onDragDrop = function(e, id) {
62  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
63  *  }
64  * </pre>
65  * @constructor
66  * @param {String} id of the element that is linked to this instance
67  * @param {String} sGroup the group of related DragDrop objects
68  * @param {object} config an object containing configurable attributes
69  *                Valid properties for DragDrop:
70  *                    padding, isTarget, maintainOffset, primaryButtonOnly
71  */
72 Roo.dd.DragDrop = function(id, sGroup, config) {
73     if (id) {
74         this.init(id, sGroup, config);
75     }
76     
77 };
78
79 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
80
81     /**
82      * The id of the element associated with this object.  This is what we
83      * refer to as the "linked element" because the size and position of
84      * this element is used to determine when the drag and drop objects have
85      * interacted.
86      * @property id
87      * @type String
88      */
89     id: null,
90
91     /**
92      * Configuration attributes passed into the constructor
93      * @property config
94      * @type object
95      */
96     config: null,
97
98     /**
99      * The id of the element that will be dragged.  By default this is same
100      * as the linked element , but could be changed to another element. Ex:
101      * Roo.dd.DDProxy
102      * @property dragElId
103      * @type String
104      * @private
105      */
106     dragElId: null,
107
108     /**
109      * the id of the element that initiates the drag operation.  By default
110      * this is the linked element, but could be changed to be a child of this
111      * element.  This lets us do things like only starting the drag when the
112      * header element within the linked html element is clicked.
113      * @property handleElId
114      * @type String
115      * @private
116      */
117     handleElId: null,
118
119     /**
120      * An associative array of HTML tags that will be ignored if clicked.
121      * @property invalidHandleTypes
122      * @type {string: string}
123      */
124     invalidHandleTypes: null,
125
126     /**
127      * An associative array of ids for elements that will be ignored if clicked
128      * @property invalidHandleIds
129      * @type {string: string}
130      */
131     invalidHandleIds: null,
132
133     /**
134      * An indexted array of css class names for elements that will be ignored
135      * if clicked.
136      * @property invalidHandleClasses
137      * @type string[]
138      */
139     invalidHandleClasses: null,
140
141     /**
142      * The linked element's absolute X position at the time the drag was
143      * started
144      * @property startPageX
145      * @type int
146      * @private
147      */
148     startPageX: 0,
149
150     /**
151      * The linked element's absolute X position at the time the drag was
152      * started
153      * @property startPageY
154      * @type int
155      * @private
156      */
157     startPageY: 0,
158
159     /**
160      * The group defines a logical collection of DragDrop objects that are
161      * related.  Instances only get events when interacting with other
162      * DragDrop object in the same group.  This lets us define multiple
163      * groups using a single DragDrop subclass if we want.
164      * @property groups
165      * @type {string: string}
166      */
167     groups: null,
168
169     /**
170      * Individual drag/drop instances can be locked.  This will prevent
171      * onmousedown start drag.
172      * @property locked
173      * @type boolean
174      * @private
175      */
176     locked: false,
177
178     /**
179      * Lock this instance
180      * @method lock
181      */
182     lock: function() { this.locked = true; },
183
184     /**
185      * Unlock this instace
186      * @method unlock
187      */
188     unlock: function() { this.locked = false; },
189
190     /**
191      * By default, all insances can be a drop target.  This can be disabled by
192      * setting isTarget to false.
193      * @method isTarget
194      * @type boolean
195      */
196     isTarget: true,
197
198     /**
199      * The padding configured for this drag and drop object for calculating
200      * the drop zone intersection with this object.
201      * @method padding
202      * @type int[]
203      */
204     padding: null,
205
206     /**
207      * Cached reference to the linked element
208      * @property _domRef
209      * @private
210      */
211     _domRef: null,
212
213     /**
214      * Internal typeof flag
215      * @property __ygDragDrop
216      * @private
217      */
218     __ygDragDrop: true,
219
220     /**
221      * Set to true when horizontal contraints are applied
222      * @property constrainX
223      * @type boolean
224      * @private
225      */
226     constrainX: false,
227
228     /**
229      * Set to true when vertical contraints are applied
230      * @property constrainY
231      * @type boolean
232      * @private
233      */
234     constrainY: false,
235
236     /**
237      * The left constraint
238      * @property minX
239      * @type int
240      * @private
241      */
242     minX: 0,
243
244     /**
245      * The right constraint
246      * @property maxX
247      * @type int
248      * @private
249      */
250     maxX: 0,
251
252     /**
253      * The up constraint
254      * @property minY
255      * @type int
256      * @type int
257      * @private
258      */
259     minY: 0,
260
261     /**
262      * The down constraint
263      * @property maxY
264      * @type int
265      * @private
266      */
267     maxY: 0,
268
269     /**
270      * Maintain offsets when we resetconstraints.  Set to true when you want
271      * the position of the element relative to its parent to stay the same
272      * when the page changes
273      *
274      * @property maintainOffset
275      * @type boolean
276      */
277     maintainOffset: false,
278
279     /**
280      * Array of pixel locations the element will snap to if we specified a
281      * horizontal graduation/interval.  This array is generated automatically
282      * when you define a tick interval.
283      * @property xTicks
284      * @type int[]
285      */
286     xTicks: null,
287
288     /**
289      * Array of pixel locations the element will snap to if we specified a
290      * vertical graduation/interval.  This array is generated automatically
291      * when you define a tick interval.
292      * @property yTicks
293      * @type int[]
294      */
295     yTicks: null,
296
297     /**
298      * By default the drag and drop instance will only respond to the primary
299      * button click (left button for a right-handed mouse).  Set to true to
300      * allow drag and drop to start with any mouse click that is propogated
301      * by the browser
302      * @property primaryButtonOnly
303      * @type boolean
304      */
305     primaryButtonOnly: true,
306
307     /**
308      * The availabe property is false until the linked dom element is accessible.
309      * @property available
310      * @type boolean
311      */
312     available: false,
313
314     /**
315      * By default, drags can only be initiated if the mousedown occurs in the
316      * region the linked element is.  This is done in part to work around a
317      * bug in some browsers that mis-report the mousedown if the previous
318      * mouseup happened outside of the window.  This property is set to true
319      * if outer handles are defined.
320      *
321      * @property hasOuterHandles
322      * @type boolean
323      * @default false
324      */
325     hasOuterHandles: false,
326
327     /**
328      * Code that executes immediately before the startDrag event
329      * @method b4StartDrag
330      * @private
331      */
332     b4StartDrag: function(x, y) { },
333
334     /**
335      * Abstract method called after a drag/drop object is clicked
336      * and the drag or mousedown time thresholds have beeen met.
337      * @method startDrag
338      * @param {int} X click location
339      * @param {int} Y click location
340      */
341     startDrag: function(x, y) { /* override this */ },
342
343     /**
344      * Code that executes immediately before the onDrag event
345      * @method b4Drag
346      * @private
347      */
348     b4Drag: function(e) { },
349
350     /**
351      * Abstract method called during the onMouseMove event while dragging an
352      * object.
353      * @method onDrag
354      * @param {Event} e the mousemove event
355      */
356     onDrag: function(e) { /* override this */ },
357
358     /**
359      * Abstract method called when this element fist begins hovering over
360      * another DragDrop obj
361      * @method onDragEnter
362      * @param {Event} e the mousemove event
363      * @param {String|DragDrop[]} id In POINT mode, the element
364      * id this is hovering over.  In INTERSECT mode, an array of one or more
365      * dragdrop items being hovered over.
366      */
367     onDragEnter: function(e, id) { /* override this */ },
368
369     /**
370      * Code that executes immediately before the onDragOver event
371      * @method b4DragOver
372      * @private
373      */
374     b4DragOver: function(e) { },
375
376     /**
377      * Abstract method called when this element is hovering over another
378      * DragDrop obj
379      * @method onDragOver
380      * @param {Event} e the mousemove event
381      * @param {String|DragDrop[]} id In POINT mode, the element
382      * id this is hovering over.  In INTERSECT mode, an array of dd items
383      * being hovered over.
384      */
385     onDragOver: function(e, id) { /* override this */ },
386
387     /**
388      * Code that executes immediately before the onDragOut event
389      * @method b4DragOut
390      * @private
391      */
392     b4DragOut: function(e) { },
393
394     /**
395      * Abstract method called when we are no longer hovering over an element
396      * @method onDragOut
397      * @param {Event} e the mousemove event
398      * @param {String|DragDrop[]} id In POINT mode, the element
399      * id this was hovering over.  In INTERSECT mode, an array of dd items
400      * that the mouse is no longer over.
401      */
402     onDragOut: function(e, id) { /* override this */ },
403
404     /**
405      * Code that executes immediately before the onDragDrop event
406      * @method b4DragDrop
407      * @private
408      */
409     b4DragDrop: function(e) { },
410
411     /**
412      * Abstract method called when this item is dropped on another DragDrop
413      * obj
414      * @method onDragDrop
415      * @param {Event} e the mouseup event
416      * @param {String|DragDrop[]} id In POINT mode, the element
417      * id this was dropped on.  In INTERSECT mode, an array of dd items this
418      * was dropped on.
419      */
420     onDragDrop: function(e, id) { /* override this */ },
421
422     /**
423      * Abstract method called when this item is dropped on an area with no
424      * drop target
425      * @method onInvalidDrop
426      * @param {Event} e the mouseup event
427      */
428     onInvalidDrop: function(e) { /* override this */ },
429
430     /**
431      * Code that executes immediately before the endDrag event
432      * @method b4EndDrag
433      * @private
434      */
435     b4EndDrag: function(e) { },
436
437     /**
438      * Fired when we are done dragging the object
439      * @method endDrag
440      * @param {Event} e the mouseup event
441      */
442     endDrag: function(e) { /* override this */ },
443
444     /**
445      * Code executed immediately before the onMouseDown event
446      * @method b4MouseDown
447      * @param {Event} e the mousedown event
448      * @private
449      */
450     b4MouseDown: function(e) {  },
451
452     /**
453      * Event handler that fires when a drag/drop obj gets a mousedown
454      * @method onMouseDown
455      * @param {Event} e the mousedown event
456      */
457     onMouseDown: function(e) { /* override this */ },
458
459     /**
460      * Event handler that fires when a drag/drop obj gets a mouseup
461      * @method onMouseUp
462      * @param {Event} e the mouseup event
463      */
464     onMouseUp: function(e) { /* override this */ },
465
466     /**
467      * Override the onAvailable method to do what is needed after the initial
468      * position was determined.
469      * @method onAvailable
470      */
471     onAvailable: function () {
472     },
473
474     /*
475      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
476      * @type Object
477      */
478     defaultPadding : {left:0, right:0, top:0, bottom:0},
479
480     /*
481      * Initializes the drag drop object's constraints to restrict movement to a certain element.
482  *
483  * Usage:
484  <pre><code>
485  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
486                 { dragElId: "existingProxyDiv" });
487  dd.startDrag = function(){
488      this.constrainTo("parent-id");
489  };
490  </code></pre>
491  * Or you can initalize it using the {@link Roo.Element} object:
492  <pre><code>
493  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
494      startDrag : function(){
495          this.constrainTo("parent-id");
496      }
497  });
498  </code></pre>
499      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
500      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
501      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
502      * an object containing the sides to pad. For example: {right:10, bottom:10}
503      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
504      */
505     constrainTo : function(constrainTo, pad, inContent){
506         if(typeof pad == "number"){
507             pad = {left: pad, right:pad, top:pad, bottom:pad};
508         }
509         pad = pad || this.defaultPadding;
510         var b = Roo.get(this.getEl()).getBox();
511         var ce = Roo.get(constrainTo);
512         var s = ce.getScroll();
513         var c, cd = ce.dom;
514         if(cd == document.body){
515             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
516         }else{
517             xy = ce.getXY();
518             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
519         }
520
521
522         var topSpace = b.y - c.y;
523         var leftSpace = b.x - c.x;
524
525         this.resetConstraints();
526         this.setXConstraint(leftSpace - (pad.left||0), // left
527                 c.width - leftSpace - b.width - (pad.right||0) //right
528         );
529         this.setYConstraint(topSpace - (pad.top||0), //top
530                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
531         );
532     },
533
534     /**
535      * Returns a reference to the linked element
536      * @method getEl
537      * @return {HTMLElement} the html element
538      */
539     getEl: function() {
540         if (!this._domRef) {
541             this._domRef = Roo.getDom(this.id);
542         }
543
544         return this._domRef;
545     },
546
547     /**
548      * Returns a reference to the actual element to drag.  By default this is
549      * the same as the html element, but it can be assigned to another
550      * element. An example of this can be found in Roo.dd.DDProxy
551      * @method getDragEl
552      * @return {HTMLElement} the html element
553      */
554     getDragEl: function() {
555         return Roo.getDom(this.dragElId);
556     },
557
558     /**
559      * Sets up the DragDrop object.  Must be called in the constructor of any
560      * Roo.dd.DragDrop subclass
561      * @method init
562      * @param id the id of the linked element
563      * @param {String} sGroup the group of related items
564      * @param {object} config configuration attributes
565      */
566     init: function(id, sGroup, config) {
567         this.initTarget(id, sGroup, config);
568         Event.on(this.id, "mousedown", this.handleMouseDown, this);
569         // Event.on(this.id, "selectstart", Event.preventDefault);
570     },
571
572     /**
573      * Initializes Targeting functionality only... the object does not
574      * get a mousedown handler.
575      * @method initTarget
576      * @param id the id of the linked element
577      * @param {String} sGroup the group of related items
578      * @param {object} config configuration attributes
579      */
580     initTarget: function(id, sGroup, config) {
581
582         // configuration attributes
583         this.config = config || {};
584
585         // create a local reference to the drag and drop manager
586         this.DDM = Roo.dd.DDM;
587         // initialize the groups array
588         this.groups = {};
589
590         // assume that we have an element reference instead of an id if the
591         // parameter is not a string
592         if (typeof id !== "string") {
593             id = Roo.id(id);
594         }
595
596         // set the id
597         this.id = id;
598
599         // add to an interaction group
600         this.addToGroup((sGroup) ? sGroup : "default");
601
602         // We don't want to register this as the handle with the manager
603         // so we just set the id rather than calling the setter.
604         this.handleElId = id;
605
606         // the linked element is the element that gets dragged by default
607         this.setDragElId(id);
608
609         // by default, clicked anchors will not start drag operations.
610         this.invalidHandleTypes = { A: "A" };
611         this.invalidHandleIds = {};
612         this.invalidHandleClasses = [];
613
614         this.applyConfig();
615
616         this.handleOnAvailable();
617     },
618
619     /**
620      * Applies the configuration parameters that were passed into the constructor.
621      * This is supposed to happen at each level through the inheritance chain.  So
622      * a DDProxy implentation will execute apply config on DDProxy, DD, and
623      * DragDrop in order to get all of the parameters that are available in
624      * each object.
625      * @method applyConfig
626      */
627     applyConfig: function() {
628
629         // configurable properties:
630         //    padding, isTarget, maintainOffset, primaryButtonOnly
631         this.padding           = this.config.padding || [0, 0, 0, 0];
632         this.isTarget          = (this.config.isTarget !== false);
633         this.maintainOffset    = (this.config.maintainOffset);
634         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
635
636     },
637
638     /**
639      * Executed when the linked element is available
640      * @method handleOnAvailable
641      * @private
642      */
643     handleOnAvailable: function() {
644         this.available = true;
645         this.resetConstraints();
646         this.onAvailable();
647     },
648
649      /**
650      * Configures the padding for the target zone in px.  Effectively expands
651      * (or reduces) the virtual object size for targeting calculations.
652      * Supports css-style shorthand; if only one parameter is passed, all sides
653      * will have that padding, and if only two are passed, the top and bottom
654      * will have the first param, the left and right the second.
655      * @method setPadding
656      * @param {int} iTop    Top pad
657      * @param {int} iRight  Right pad
658      * @param {int} iBot    Bot pad
659      * @param {int} iLeft   Left pad
660      */
661     setPadding: function(iTop, iRight, iBot, iLeft) {
662         // this.padding = [iLeft, iRight, iTop, iBot];
663         if (!iRight && 0 !== iRight) {
664             this.padding = [iTop, iTop, iTop, iTop];
665         } else if (!iBot && 0 !== iBot) {
666             this.padding = [iTop, iRight, iTop, iRight];
667         } else {
668             this.padding = [iTop, iRight, iBot, iLeft];
669         }
670     },
671
672     /**
673      * Stores the initial placement of the linked element.
674      * @method setInitialPosition
675      * @param {int} diffX   the X offset, default 0
676      * @param {int} diffY   the Y offset, default 0
677      */
678     setInitPosition: function(diffX, diffY) {
679         var el = this.getEl();
680
681         if (!this.DDM.verifyEl(el)) {
682             return;
683         }
684
685         var dx = diffX || 0;
686         var dy = diffY || 0;
687
688         var p = Dom.getXY( el );
689
690         this.initPageX = p[0] - dx;
691         this.initPageY = p[1] - dy;
692
693         this.lastPageX = p[0];
694         this.lastPageY = p[1];
695
696
697         this.setStartPosition(p);
698     },
699
700     /**
701      * Sets the start position of the element.  This is set when the obj
702      * is initialized, the reset when a drag is started.
703      * @method setStartPosition
704      * @param pos current position (from previous lookup)
705      * @private
706      */
707     setStartPosition: function(pos) {
708         var p = pos || Dom.getXY( this.getEl() );
709         this.deltaSetXY = null;
710
711         this.startPageX = p[0];
712         this.startPageY = p[1];
713     },
714
715     /**
716      * Add this instance to a group of related drag/drop objects.  All
717      * instances belong to at least one group, and can belong to as many
718      * groups as needed.
719      * @method addToGroup
720      * @param sGroup {string} the name of the group
721      */
722     addToGroup: function(sGroup) {
723         this.groups[sGroup] = true;
724         this.DDM.regDragDrop(this, sGroup);
725     },
726
727     /**
728      * Remove's this instance from the supplied interaction group
729      * @method removeFromGroup
730      * @param {string}  sGroup  The group to drop
731      */
732     removeFromGroup: function(sGroup) {
733         if (this.groups[sGroup]) {
734             delete this.groups[sGroup];
735         }
736
737         this.DDM.removeDDFromGroup(this, sGroup);
738     },
739
740     /**
741      * Allows you to specify that an element other than the linked element
742      * will be moved with the cursor during a drag
743      * @method setDragElId
744      * @param id {string} the id of the element that will be used to initiate the drag
745      */
746     setDragElId: function(id) {
747         this.dragElId = id;
748     },
749
750     /**
751      * Allows you to specify a child of the linked element that should be
752      * used to initiate the drag operation.  An example of this would be if
753      * you have a content div with text and links.  Clicking anywhere in the
754      * content area would normally start the drag operation.  Use this method
755      * to specify that an element inside of the content div is the element
756      * that starts the drag operation.
757      * @method setHandleElId
758      * @param id {string} the id of the element that will be used to
759      * initiate the drag.
760      */
761     setHandleElId: function(id) {
762         if (typeof id !== "string") {
763             id = Roo.id(id);
764         }
765         this.handleElId = id;
766         this.DDM.regHandle(this.id, id);
767     },
768
769     /**
770      * Allows you to set an element outside of the linked element as a drag
771      * handle
772      * @method setOuterHandleElId
773      * @param id the id of the element that will be used to initiate the drag
774      */
775     setOuterHandleElId: function(id) {
776         if (typeof id !== "string") {
777             id = Roo.id(id);
778         }
779         Event.on(id, "mousedown",
780                 this.handleMouseDown, this);
781         this.setHandleElId(id);
782
783         this.hasOuterHandles = true;
784     },
785
786     /**
787      * Remove all drag and drop hooks for this element
788      * @method unreg
789      */
790     unreg: function() {
791         Event.un(this.id, "mousedown",
792                 this.handleMouseDown);
793         this._domRef = null;
794         this.DDM._remove(this);
795     },
796
797     destroy : function(){
798         this.unreg();
799     },
800
801     /**
802      * Returns true if this instance is locked, or the drag drop mgr is locked
803      * (meaning that all drag/drop is disabled on the page.)
804      * @method isLocked
805      * @return {boolean} true if this obj or all drag/drop is locked, else
806      * false
807      */
808     isLocked: function() {
809         return (this.DDM.isLocked() || this.locked);
810     },
811
812     /**
813      * Fired when this object is clicked
814      * @method handleMouseDown
815      * @param {Event} e
816      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
817      * @private
818      */
819     handleMouseDown: function(e, oDD){
820         if (this.primaryButtonOnly && e.button != 0) {
821             return;
822         }
823
824         if (this.isLocked()) {
825             return;
826         }
827
828         this.DDM.refreshCache(this.groups);
829
830         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
831         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
832         } else {
833             if (this.clickValidator(e)) {
834
835                 // set the initial element position
836                 this.setStartPosition();
837
838
839                 this.b4MouseDown(e);
840                 this.onMouseDown(e);
841
842                 this.DDM.handleMouseDown(e, this);
843
844                 this.DDM.stopEvent(e);
845             } else {
846
847
848             }
849         }
850     },
851
852     clickValidator: function(e) {
853         var target = e.getTarget();
854         return ( this.isValidHandleChild(target) &&
855                     (this.id == this.handleElId ||
856                         this.DDM.handleWasClicked(target, this.id)) );
857     },
858
859     /**
860      * Allows you to specify a tag name that should not start a drag operation
861      * when clicked.  This is designed to facilitate embedding links within a
862      * drag handle that do something other than start the drag.
863      * @method addInvalidHandleType
864      * @param {string} tagName the type of element to exclude
865      */
866     addInvalidHandleType: function(tagName) {
867         var type = tagName.toUpperCase();
868         this.invalidHandleTypes[type] = type;
869     },
870
871     /**
872      * Lets you to specify an element id for a child of a drag handle
873      * that should not initiate a drag
874      * @method addInvalidHandleId
875      * @param {string} id the element id of the element you wish to ignore
876      */
877     addInvalidHandleId: function(id) {
878         if (typeof id !== "string") {
879             id = Roo.id(id);
880         }
881         this.invalidHandleIds[id] = id;
882     },
883
884     /**
885      * Lets you specify a css class of elements that will not initiate a drag
886      * @method addInvalidHandleClass
887      * @param {string} cssClass the class of the elements you wish to ignore
888      */
889     addInvalidHandleClass: function(cssClass) {
890         this.invalidHandleClasses.push(cssClass);
891     },
892
893     /**
894      * Unsets an excluded tag name set by addInvalidHandleType
895      * @method removeInvalidHandleType
896      * @param {string} tagName the type of element to unexclude
897      */
898     removeInvalidHandleType: function(tagName) {
899         var type = tagName.toUpperCase();
900         // this.invalidHandleTypes[type] = null;
901         delete this.invalidHandleTypes[type];
902     },
903
904     /**
905      * Unsets an invalid handle id
906      * @method removeInvalidHandleId
907      * @param {string} id the id of the element to re-enable
908      */
909     removeInvalidHandleId: function(id) {
910         if (typeof id !== "string") {
911             id = Roo.id(id);
912         }
913         delete this.invalidHandleIds[id];
914     },
915
916     /**
917      * Unsets an invalid css class
918      * @method removeInvalidHandleClass
919      * @param {string} cssClass the class of the element(s) you wish to
920      * re-enable
921      */
922     removeInvalidHandleClass: function(cssClass) {
923         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
924             if (this.invalidHandleClasses[i] == cssClass) {
925                 delete this.invalidHandleClasses[i];
926             }
927         }
928     },
929
930     /**
931      * Checks the tag exclusion list to see if this click should be ignored
932      * @method isValidHandleChild
933      * @param {HTMLElement} node the HTMLElement to evaluate
934      * @return {boolean} true if this is a valid tag type, false if not
935      */
936     isValidHandleChild: function(node) {
937
938         var valid = true;
939         // var n = (node.nodeName == "#text") ? node.parentNode : node;
940         var nodeName;
941         try {
942             nodeName = node.nodeName.toUpperCase();
943         } catch(e) {
944             nodeName = node.nodeName;
945         }
946         valid = valid && !this.invalidHandleTypes[nodeName];
947         valid = valid && !this.invalidHandleIds[node.id];
948
949         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
950             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
951         }
952
953
954         return valid;
955
956     },
957
958     /**
959      * Create the array of horizontal tick marks if an interval was specified
960      * in setXConstraint().
961      * @method setXTicks
962      * @private
963      */
964     setXTicks: function(iStartX, iTickSize) {
965         this.xTicks = [];
966         this.xTickSize = iTickSize;
967
968         var tickMap = {};
969
970         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
971             if (!tickMap[i]) {
972                 this.xTicks[this.xTicks.length] = i;
973                 tickMap[i] = true;
974             }
975         }
976
977         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
978             if (!tickMap[i]) {
979                 this.xTicks[this.xTicks.length] = i;
980                 tickMap[i] = true;
981             }
982         }
983
984         this.xTicks.sort(this.DDM.numericSort) ;
985     },
986
987     /**
988      * Create the array of vertical tick marks if an interval was specified in
989      * setYConstraint().
990      * @method setYTicks
991      * @private
992      */
993     setYTicks: function(iStartY, iTickSize) {
994         this.yTicks = [];
995         this.yTickSize = iTickSize;
996
997         var tickMap = {};
998
999         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
1000             if (!tickMap[i]) {
1001                 this.yTicks[this.yTicks.length] = i;
1002                 tickMap[i] = true;
1003             }
1004         }
1005
1006         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1007             if (!tickMap[i]) {
1008                 this.yTicks[this.yTicks.length] = i;
1009                 tickMap[i] = true;
1010             }
1011         }
1012
1013         this.yTicks.sort(this.DDM.numericSort) ;
1014     },
1015
1016     /**
1017      * By default, the element can be dragged any place on the screen.  Use
1018      * this method to limit the horizontal travel of the element.  Pass in
1019      * 0,0 for the parameters if you want to lock the drag to the y axis.
1020      * @method setXConstraint
1021      * @param {int} iLeft the number of pixels the element can move to the left
1022      * @param {int} iRight the number of pixels the element can move to the
1023      * right
1024      * @param {int} iTickSize optional parameter for specifying that the
1025      * element
1026      * should move iTickSize pixels at a time.
1027      */
1028     setXConstraint: function(iLeft, iRight, iTickSize) {
1029         this.leftConstraint = iLeft;
1030         this.rightConstraint = iRight;
1031
1032         this.minX = this.initPageX - iLeft;
1033         this.maxX = this.initPageX + iRight;
1034         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1035
1036         this.constrainX = true;
1037     },
1038
1039     /**
1040      * Clears any constraints applied to this instance.  Also clears ticks
1041      * since they can't exist independent of a constraint at this time.
1042      * @method clearConstraints
1043      */
1044     clearConstraints: function() {
1045         this.constrainX = false;
1046         this.constrainY = false;
1047         this.clearTicks();
1048     },
1049
1050     /**
1051      * Clears any tick interval defined for this instance
1052      * @method clearTicks
1053      */
1054     clearTicks: function() {
1055         this.xTicks = null;
1056         this.yTicks = null;
1057         this.xTickSize = 0;
1058         this.yTickSize = 0;
1059     },
1060
1061     /**
1062      * By default, the element can be dragged any place on the screen.  Set
1063      * this to limit the vertical travel of the element.  Pass in 0,0 for the
1064      * parameters if you want to lock the drag to the x axis.
1065      * @method setYConstraint
1066      * @param {int} iUp the number of pixels the element can move up
1067      * @param {int} iDown the number of pixels the element can move down
1068      * @param {int} iTickSize optional parameter for specifying that the
1069      * element should move iTickSize pixels at a time.
1070      */
1071     setYConstraint: function(iUp, iDown, iTickSize) {
1072         this.topConstraint = iUp;
1073         this.bottomConstraint = iDown;
1074
1075         this.minY = this.initPageY - iUp;
1076         this.maxY = this.initPageY + iDown;
1077         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1078
1079         this.constrainY = true;
1080
1081     },
1082
1083     /**
1084      * resetConstraints must be called if you manually reposition a dd element.
1085      * @method resetConstraints
1086      * @param {boolean} maintainOffset
1087      */
1088     resetConstraints: function() {
1089
1090
1091         // Maintain offsets if necessary
1092         if (this.initPageX || this.initPageX === 0) {
1093             // figure out how much this thing has moved
1094             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1095             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1096
1097             this.setInitPosition(dx, dy);
1098
1099         // This is the first time we have detected the element's position
1100         } else {
1101             this.setInitPosition();
1102         }
1103
1104         if (this.constrainX) {
1105             this.setXConstraint( this.leftConstraint,
1106                                  this.rightConstraint,
1107                                  this.xTickSize        );
1108         }
1109
1110         if (this.constrainY) {
1111             this.setYConstraint( this.topConstraint,
1112                                  this.bottomConstraint,
1113                                  this.yTickSize         );
1114         }
1115     },
1116
1117     /**
1118      * Normally the drag element is moved pixel by pixel, but we can specify
1119      * that it move a number of pixels at a time.  This method resolves the
1120      * location when we have it set up like this.
1121      * @method getTick
1122      * @param {int} val where we want to place the object
1123      * @param {int[]} tickArray sorted array of valid points
1124      * @return {int} the closest tick
1125      * @private
1126      */
1127     getTick: function(val, tickArray) {
1128
1129         if (!tickArray) {
1130             // If tick interval is not defined, it is effectively 1 pixel,
1131             // so we return the value passed to us.
1132             return val;
1133         } else if (tickArray[0] >= val) {
1134             // The value is lower than the first tick, so we return the first
1135             // tick.
1136             return tickArray[0];
1137         } else {
1138             for (var i=0, len=tickArray.length; i<len; ++i) {
1139                 var next = i + 1;
1140                 if (tickArray[next] && tickArray[next] >= val) {
1141                     var diff1 = val - tickArray[i];
1142                     var diff2 = tickArray[next] - val;
1143                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1144                 }
1145             }
1146
1147             // The value is larger than the last tick, so we return the last
1148             // tick.
1149             return tickArray[tickArray.length - 1];
1150         }
1151     },
1152
1153     /**
1154      * toString method
1155      * @method toString
1156      * @return {string} string representation of the dd obj
1157      */
1158     toString: function() {
1159         return ("DragDrop " + this.id);
1160     }
1161
1162 });
1163
1164 })();
1165 /*
1166  * Based on:
1167  * Ext JS Library 1.1.1
1168  * Copyright(c) 2006-2007, Ext JS, LLC.
1169  *
1170  * Originally Released Under LGPL - original licence link has changed is not relivant.
1171  *
1172  * Fork - LGPL
1173  * <script type="text/javascript">
1174  */
1175
1176
1177 /**
1178  * The drag and drop utility provides a framework for building drag and drop
1179  * applications.  In addition to enabling drag and drop for specific elements,
1180  * the drag and drop elements are tracked by the manager class, and the
1181  * interactions between the various elements are tracked during the drag and
1182  * the implementing code is notified about these important moments.
1183  */
1184
1185 // Only load the library once.  Rewriting the manager class would orphan
1186 // existing drag and drop instances.
1187 if (!Roo.dd.DragDropMgr) {
1188
1189 /**
1190  * @class Roo.dd.DragDropMgr
1191  * DragDropMgr is a singleton that tracks the element interaction for
1192  * all DragDrop items in the window.  Generally, you will not call
1193  * this class directly, but it does have helper methods that could
1194  * be useful in your DragDrop implementations.
1195  * @singleton
1196  */
1197 Roo.dd.DragDropMgr = function() {
1198
1199     var Event = Roo.EventManager;
1200
1201     return {
1202
1203         /**
1204          * Two dimensional Array of registered DragDrop objects.  The first
1205          * dimension is the DragDrop item group, the second the DragDrop
1206          * object.
1207          * @property ids
1208          * @type {string: string}
1209          * @private
1210          * @static
1211          */
1212         ids: {},
1213
1214         /**
1215          * Array of element ids defined as drag handles.  Used to determine
1216          * if the element that generated the mousedown event is actually the
1217          * handle and not the html element itself.
1218          * @property handleIds
1219          * @type {string: string}
1220          * @private
1221          * @static
1222          */
1223         handleIds: {},
1224
1225         /**
1226          * the DragDrop object that is currently being dragged
1227          * @property dragCurrent
1228          * @type DragDrop
1229          * @private
1230          * @static
1231          **/
1232         dragCurrent: null,
1233
1234         /**
1235          * the DragDrop object(s) that are being hovered over
1236          * @property dragOvers
1237          * @type Array
1238          * @private
1239          * @static
1240          */
1241         dragOvers: {},
1242
1243         /**
1244          * the X distance between the cursor and the object being dragged
1245          * @property deltaX
1246          * @type int
1247          * @private
1248          * @static
1249          */
1250         deltaX: 0,
1251
1252         /**
1253          * the Y distance between the cursor and the object being dragged
1254          * @property deltaY
1255          * @type int
1256          * @private
1257          * @static
1258          */
1259         deltaY: 0,
1260
1261         /**
1262          * Flag to determine if we should prevent the default behavior of the
1263          * events we define. By default this is true, but this can be set to
1264          * false if you need the default behavior (not recommended)
1265          * @property preventDefault
1266          * @type boolean
1267          * @static
1268          */
1269         preventDefault: true,
1270
1271         /**
1272          * Flag to determine if we should stop the propagation of the events
1273          * we generate. This is true by default but you may want to set it to
1274          * false if the html element contains other features that require the
1275          * mouse click.
1276          * @property stopPropagation
1277          * @type boolean
1278          * @static
1279          */
1280         stopPropagation: true,
1281
1282         /**
1283          * Internal flag that is set to true when drag and drop has been
1284          * intialized
1285          * @property initialized
1286          * @private
1287          * @static
1288          */
1289         initalized: false,
1290
1291         /**
1292          * All drag and drop can be disabled.
1293          * @property locked
1294          * @private
1295          * @static
1296          */
1297         locked: false,
1298
1299         /**
1300          * Called the first time an element is registered.
1301          * @method init
1302          * @private
1303          * @static
1304          */
1305         init: function() {
1306             this.initialized = true;
1307         },
1308
1309         /**
1310          * In point mode, drag and drop interaction is defined by the
1311          * location of the cursor during the drag/drop
1312          * @property POINT
1313          * @type int
1314          * @static
1315          */
1316         POINT: 0,
1317
1318         /**
1319          * In intersect mode, drag and drop interactio nis defined by the
1320          * overlap of two or more drag and drop objects.
1321          * @property INTERSECT
1322          * @type int
1323          * @static
1324          */
1325         INTERSECT: 1,
1326
1327         /**
1328          * The current drag and drop mode.  Default: POINT
1329          * @property mode
1330          * @type int
1331          * @static
1332          */
1333         mode: 0,
1334
1335         /**
1336          * Runs method on all drag and drop objects
1337          * @method _execOnAll
1338          * @private
1339          * @static
1340          */
1341         _execOnAll: function(sMethod, args) {
1342             for (var i in this.ids) {
1343                 for (var j in this.ids[i]) {
1344                     var oDD = this.ids[i][j];
1345                     if (! this.isTypeOfDD(oDD)) {
1346                         continue;
1347                     }
1348                     oDD[sMethod].apply(oDD, args);
1349                 }
1350             }
1351         },
1352
1353         /**
1354          * Drag and drop initialization.  Sets up the global event handlers
1355          * @method _onLoad
1356          * @private
1357          * @static
1358          */
1359         _onLoad: function() {
1360
1361             this.init();
1362
1363
1364             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
1365             Event.on(document, "mousemove", this.handleMouseMove, this, true);
1366             Event.on(window,   "unload",    this._onUnload, this, true);
1367             Event.on(window,   "resize",    this._onResize, this, true);
1368             // Event.on(window,   "mouseout",    this._test);
1369
1370         },
1371
1372         /**
1373          * Reset constraints on all drag and drop objs
1374          * @method _onResize
1375          * @private
1376          * @static
1377          */
1378         _onResize: function(e) {
1379             this._execOnAll("resetConstraints", []);
1380         },
1381
1382         /**
1383          * Lock all drag and drop functionality
1384          * @method lock
1385          * @static
1386          */
1387         lock: function() { this.locked = true; },
1388
1389         /**
1390          * Unlock all drag and drop functionality
1391          * @method unlock
1392          * @static
1393          */
1394         unlock: function() { this.locked = false; },
1395
1396         /**
1397          * Is drag and drop locked?
1398          * @method isLocked
1399          * @return {boolean} True if drag and drop is locked, false otherwise.
1400          * @static
1401          */
1402         isLocked: function() { return this.locked; },
1403
1404         /**
1405          * Location cache that is set for all drag drop objects when a drag is
1406          * initiated, cleared when the drag is finished.
1407          * @property locationCache
1408          * @private
1409          * @static
1410          */
1411         locationCache: {},
1412
1413         /**
1414          * Set useCache to false if you want to force object the lookup of each
1415          * drag and drop linked element constantly during a drag.
1416          * @property useCache
1417          * @type boolean
1418          * @static
1419          */
1420         useCache: true,
1421
1422         /**
1423          * The number of pixels that the mouse needs to move after the
1424          * mousedown before the drag is initiated.  Default=3;
1425          * @property clickPixelThresh
1426          * @type int
1427          * @static
1428          */
1429         clickPixelThresh: 3,
1430
1431         /**
1432          * The number of milliseconds after the mousedown event to initiate the
1433          * drag if we don't get a mouseup event. Default=1000
1434          * @property clickTimeThresh
1435          * @type int
1436          * @static
1437          */
1438         clickTimeThresh: 350,
1439
1440         /**
1441          * Flag that indicates that either the drag pixel threshold or the
1442          * mousdown time threshold has been met
1443          * @property dragThreshMet
1444          * @type boolean
1445          * @private
1446          * @static
1447          */
1448         dragThreshMet: false,
1449
1450         /**
1451          * Timeout used for the click time threshold
1452          * @property clickTimeout
1453          * @type Object
1454          * @private
1455          * @static
1456          */
1457         clickTimeout: null,
1458
1459         /**
1460          * The X position of the mousedown event stored for later use when a
1461          * drag threshold is met.
1462          * @property startX
1463          * @type int
1464          * @private
1465          * @static
1466          */
1467         startX: 0,
1468
1469         /**
1470          * The Y position of the mousedown event stored for later use when a
1471          * drag threshold is met.
1472          * @property startY
1473          * @type int
1474          * @private
1475          * @static
1476          */
1477         startY: 0,
1478
1479         /**
1480          * Each DragDrop instance must be registered with the DragDropMgr.
1481          * This is executed in DragDrop.init()
1482          * @method regDragDrop
1483          * @param {DragDrop} oDD the DragDrop object to register
1484          * @param {String} sGroup the name of the group this element belongs to
1485          * @static
1486          */
1487         regDragDrop: function(oDD, sGroup) {
1488             if (!this.initialized) { this.init(); }
1489
1490             if (!this.ids[sGroup]) {
1491                 this.ids[sGroup] = {};
1492             }
1493             this.ids[sGroup][oDD.id] = oDD;
1494         },
1495
1496         /**
1497          * Removes the supplied dd instance from the supplied group. Executed
1498          * by DragDrop.removeFromGroup, so don't call this function directly.
1499          * @method removeDDFromGroup
1500          * @private
1501          * @static
1502          */
1503         removeDDFromGroup: function(oDD, sGroup) {
1504             if (!this.ids[sGroup]) {
1505                 this.ids[sGroup] = {};
1506             }
1507
1508             var obj = this.ids[sGroup];
1509             if (obj && obj[oDD.id]) {
1510                 delete obj[oDD.id];
1511             }
1512         },
1513
1514         /**
1515          * Unregisters a drag and drop item.  This is executed in
1516          * DragDrop.unreg, use that method instead of calling this directly.
1517          * @method _remove
1518          * @private
1519          * @static
1520          */
1521         _remove: function(oDD) {
1522             for (var g in oDD.groups) {
1523                 if (g && this.ids[g][oDD.id]) {
1524                     delete this.ids[g][oDD.id];
1525                 }
1526             }
1527             delete this.handleIds[oDD.id];
1528         },
1529
1530         /**
1531          * Each DragDrop handle element must be registered.  This is done
1532          * automatically when executing DragDrop.setHandleElId()
1533          * @method regHandle
1534          * @param {String} sDDId the DragDrop id this element is a handle for
1535          * @param {String} sHandleId the id of the element that is the drag
1536          * handle
1537          * @static
1538          */
1539         regHandle: function(sDDId, sHandleId) {
1540             if (!this.handleIds[sDDId]) {
1541                 this.handleIds[sDDId] = {};
1542             }
1543             this.handleIds[sDDId][sHandleId] = sHandleId;
1544         },
1545
1546         /**
1547          * Utility function to determine if a given element has been
1548          * registered as a drag drop item.
1549          * @method isDragDrop
1550          * @param {String} id the element id to check
1551          * @return {boolean} true if this element is a DragDrop item,
1552          * false otherwise
1553          * @static
1554          */
1555         isDragDrop: function(id) {
1556             return ( this.getDDById(id) ) ? true : false;
1557         },
1558
1559         /**
1560          * Returns the drag and drop instances that are in all groups the
1561          * passed in instance belongs to.
1562          * @method getRelated
1563          * @param {DragDrop} p_oDD the obj to get related data for
1564          * @param {boolean} bTargetsOnly if true, only return targetable objs
1565          * @return {DragDrop[]} the related instances
1566          * @static
1567          */
1568         getRelated: function(p_oDD, bTargetsOnly) {
1569             var oDDs = [];
1570             for (var i in p_oDD.groups) {
1571                 for (j in this.ids[i]) {
1572                     var dd = this.ids[i][j];
1573                     if (! this.isTypeOfDD(dd)) {
1574                         continue;
1575                     }
1576                     if (!bTargetsOnly || dd.isTarget) {
1577                         oDDs[oDDs.length] = dd;
1578                     }
1579                 }
1580             }
1581
1582             return oDDs;
1583         },
1584
1585         /**
1586          * Returns true if the specified dd target is a legal target for
1587          * the specifice drag obj
1588          * @method isLegalTarget
1589          * @param {DragDrop} the drag obj
1590          * @param {DragDrop} the target
1591          * @return {boolean} true if the target is a legal target for the
1592          * dd obj
1593          * @static
1594          */
1595         isLegalTarget: function (oDD, oTargetDD) {
1596             var targets = this.getRelated(oDD, true);
1597             for (var i=0, len=targets.length;i<len;++i) {
1598                 if (targets[i].id == oTargetDD.id) {
1599                     return true;
1600                 }
1601             }
1602
1603             return false;
1604         },
1605
1606         /**
1607          * My goal is to be able to transparently determine if an object is
1608          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
1609          * returns "object", oDD.constructor.toString() always returns
1610          * "DragDrop" and not the name of the subclass.  So for now it just
1611          * evaluates a well-known variable in DragDrop.
1612          * @method isTypeOfDD
1613          * @param {Object} the object to evaluate
1614          * @return {boolean} true if typeof oDD = DragDrop
1615          * @static
1616          */
1617         isTypeOfDD: function (oDD) {
1618             return (oDD && oDD.__ygDragDrop);
1619         },
1620
1621         /**
1622          * Utility function to determine if a given element has been
1623          * registered as a drag drop handle for the given Drag Drop object.
1624          * @method isHandle
1625          * @param {String} id the element id to check
1626          * @return {boolean} true if this element is a DragDrop handle, false
1627          * otherwise
1628          * @static
1629          */
1630         isHandle: function(sDDId, sHandleId) {
1631             return ( this.handleIds[sDDId] &&
1632                             this.handleIds[sDDId][sHandleId] );
1633         },
1634
1635         /**
1636          * Returns the DragDrop instance for a given id
1637          * @method getDDById
1638          * @param {String} id the id of the DragDrop object
1639          * @return {DragDrop} the drag drop object, null if it is not found
1640          * @static
1641          */
1642         getDDById: function(id) {
1643             for (var i in this.ids) {
1644                 if (this.ids[i][id]) {
1645                     return this.ids[i][id];
1646                 }
1647             }
1648             return null;
1649         },
1650
1651         /**
1652          * Fired after a registered DragDrop object gets the mousedown event.
1653          * Sets up the events required to track the object being dragged
1654          * @method handleMouseDown
1655          * @param {Event} e the event
1656          * @param oDD the DragDrop object being dragged
1657          * @private
1658          * @static
1659          */
1660         handleMouseDown: function(e, oDD) {
1661             if(Roo.QuickTips){
1662                 Roo.QuickTips.disable();
1663             }
1664             this.currentTarget = e.getTarget();
1665
1666             this.dragCurrent = oDD;
1667
1668             var el = oDD.getEl();
1669
1670             // track start position
1671             this.startX = e.getPageX();
1672             this.startY = e.getPageY();
1673
1674             this.deltaX = this.startX - el.offsetLeft;
1675             this.deltaY = this.startY - el.offsetTop;
1676
1677             this.dragThreshMet = false;
1678
1679             this.clickTimeout = setTimeout(
1680                     function() {
1681                         var DDM = Roo.dd.DDM;
1682                         DDM.startDrag(DDM.startX, DDM.startY);
1683                     },
1684                     this.clickTimeThresh );
1685         },
1686
1687         /**
1688          * Fired when either the drag pixel threshol or the mousedown hold
1689          * time threshold has been met.
1690          * @method startDrag
1691          * @param x {int} the X position of the original mousedown
1692          * @param y {int} the Y position of the original mousedown
1693          * @static
1694          */
1695         startDrag: function(x, y) {
1696             clearTimeout(this.clickTimeout);
1697             if (this.dragCurrent) {
1698                 this.dragCurrent.b4StartDrag(x, y);
1699                 this.dragCurrent.startDrag(x, y);
1700             }
1701             this.dragThreshMet = true;
1702         },
1703
1704         /**
1705          * Internal function to handle the mouseup event.  Will be invoked
1706          * from the context of the document.
1707          * @method handleMouseUp
1708          * @param {Event} e the event
1709          * @private
1710          * @static
1711          */
1712         handleMouseUp: function(e) {
1713
1714             if(Roo.QuickTips){
1715                 Roo.QuickTips.enable();
1716             }
1717             if (! this.dragCurrent) {
1718                 return;
1719             }
1720
1721             clearTimeout(this.clickTimeout);
1722
1723             if (this.dragThreshMet) {
1724                 this.fireEvents(e, true);
1725             } else {
1726             }
1727
1728             this.stopDrag(e);
1729
1730             this.stopEvent(e);
1731         },
1732
1733         /**
1734          * Utility to stop event propagation and event default, if these
1735          * features are turned on.
1736          * @method stopEvent
1737          * @param {Event} e the event as returned by this.getEvent()
1738          * @static
1739          */
1740         stopEvent: function(e){
1741             if(this.stopPropagation) {
1742                 e.stopPropagation();
1743             }
1744
1745             if (this.preventDefault) {
1746                 e.preventDefault();
1747             }
1748         },
1749
1750         /**
1751          * Internal function to clean up event handlers after the drag
1752          * operation is complete
1753          * @method stopDrag
1754          * @param {Event} e the event
1755          * @private
1756          * @static
1757          */
1758         stopDrag: function(e) {
1759             // Fire the drag end event for the item that was dragged
1760             if (this.dragCurrent) {
1761                 if (this.dragThreshMet) {
1762                     this.dragCurrent.b4EndDrag(e);
1763                     this.dragCurrent.endDrag(e);
1764                 }
1765
1766                 this.dragCurrent.onMouseUp(e);
1767             }
1768
1769             this.dragCurrent = null;
1770             this.dragOvers = {};
1771         },
1772
1773         /**
1774          * Internal function to handle the mousemove event.  Will be invoked
1775          * from the context of the html element.
1776          *
1777          * @TODO figure out what we can do about mouse events lost when the
1778          * user drags objects beyond the window boundary.  Currently we can
1779          * detect this in internet explorer by verifying that the mouse is
1780          * down during the mousemove event.  Firefox doesn't give us the
1781          * button state on the mousemove event.
1782          * @method handleMouseMove
1783          * @param {Event} e the event
1784          * @private
1785          * @static
1786          */
1787         handleMouseMove: function(e) {
1788             if (! this.dragCurrent) {
1789                 return true;
1790             }
1791
1792             // var button = e.which || e.button;
1793
1794             // check for IE mouseup outside of page boundary
1795             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1796                 this.stopEvent(e);
1797                 return this.handleMouseUp(e);
1798             }
1799
1800             if (!this.dragThreshMet) {
1801                 var diffX = Math.abs(this.startX - e.getPageX());
1802                 var diffY = Math.abs(this.startY - e.getPageY());
1803                 if (diffX > this.clickPixelThresh ||
1804                             diffY > this.clickPixelThresh) {
1805                     this.startDrag(this.startX, this.startY);
1806                 }
1807             }
1808
1809             if (this.dragThreshMet) {
1810                 this.dragCurrent.b4Drag(e);
1811                 this.dragCurrent.onDrag(e);
1812                 if(!this.dragCurrent.moveOnly){
1813                     this.fireEvents(e, false);
1814                 }
1815             }
1816
1817             this.stopEvent(e);
1818
1819             return true;
1820         },
1821
1822         /**
1823          * Iterates over all of the DragDrop elements to find ones we are
1824          * hovering over or dropping on
1825          * @method fireEvents
1826          * @param {Event} e the event
1827          * @param {boolean} isDrop is this a drop op or a mouseover op?
1828          * @private
1829          * @static
1830          */
1831         fireEvents: function(e, isDrop) {
1832             var dc = this.dragCurrent;
1833
1834             // If the user did the mouse up outside of the window, we could
1835             // get here even though we have ended the drag.
1836             if (!dc || dc.isLocked()) {
1837                 return;
1838             }
1839
1840             var pt = e.getPoint();
1841
1842             // cache the previous dragOver array
1843             var oldOvers = [];
1844
1845             var outEvts   = [];
1846             var overEvts  = [];
1847             var dropEvts  = [];
1848             var enterEvts = [];
1849
1850             // Check to see if the object(s) we were hovering over is no longer
1851             // being hovered over so we can fire the onDragOut event
1852             for (var i in this.dragOvers) {
1853
1854                 var ddo = this.dragOvers[i];
1855
1856                 if (! this.isTypeOfDD(ddo)) {
1857                     continue;
1858                 }
1859
1860                 if (! this.isOverTarget(pt, ddo, this.mode)) {
1861                     outEvts.push( ddo );
1862                 }
1863
1864                 oldOvers[i] = true;
1865                 delete this.dragOvers[i];
1866             }
1867
1868             for (var sGroup in dc.groups) {
1869
1870                 if ("string" != typeof sGroup) {
1871                     continue;
1872                 }
1873
1874                 for (i in this.ids[sGroup]) {
1875                     var oDD = this.ids[sGroup][i];
1876                     if (! this.isTypeOfDD(oDD)) {
1877                         continue;
1878                     }
1879
1880                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1881                         if (this.isOverTarget(pt, oDD, this.mode)) {
1882                             // look for drop interactions
1883                             if (isDrop) {
1884                                 dropEvts.push( oDD );
1885                             // look for drag enter and drag over interactions
1886                             } else {
1887
1888                                 // initial drag over: dragEnter fires
1889                                 if (!oldOvers[oDD.id]) {
1890                                     enterEvts.push( oDD );
1891                                 // subsequent drag overs: dragOver fires
1892                                 } else {
1893                                     overEvts.push( oDD );
1894                                 }
1895
1896                                 this.dragOvers[oDD.id] = oDD;
1897                             }
1898                         }
1899                     }
1900                 }
1901             }
1902
1903             if (this.mode) {
1904                 if (outEvts.length) {
1905                     dc.b4DragOut(e, outEvts);
1906                     dc.onDragOut(e, outEvts);
1907                 }
1908
1909                 if (enterEvts.length) {
1910                     dc.onDragEnter(e, enterEvts);
1911                 }
1912
1913                 if (overEvts.length) {
1914                     dc.b4DragOver(e, overEvts);
1915                     dc.onDragOver(e, overEvts);
1916                 }
1917
1918                 if (dropEvts.length) {
1919                     dc.b4DragDrop(e, dropEvts);
1920                     dc.onDragDrop(e, dropEvts);
1921                 }
1922
1923             } else {
1924                 // fire dragout events
1925                 var len = 0;
1926                 for (i=0, len=outEvts.length; i<len; ++i) {
1927                     dc.b4DragOut(e, outEvts[i].id);
1928                     dc.onDragOut(e, outEvts[i].id);
1929                 }
1930
1931                 // fire enter events
1932                 for (i=0,len=enterEvts.length; i<len; ++i) {
1933                     // dc.b4DragEnter(e, oDD.id);
1934                     dc.onDragEnter(e, enterEvts[i].id);
1935                 }
1936
1937                 // fire over events
1938                 for (i=0,len=overEvts.length; i<len; ++i) {
1939                     dc.b4DragOver(e, overEvts[i].id);
1940                     dc.onDragOver(e, overEvts[i].id);
1941                 }
1942
1943                 // fire drop events
1944                 for (i=0, len=dropEvts.length; i<len; ++i) {
1945                     dc.b4DragDrop(e, dropEvts[i].id);
1946                     dc.onDragDrop(e, dropEvts[i].id);
1947                 }
1948
1949             }
1950
1951             // notify about a drop that did not find a target
1952             if (isDrop && !dropEvts.length) {
1953                 dc.onInvalidDrop(e);
1954             }
1955
1956         },
1957
1958         /**
1959          * Helper function for getting the best match from the list of drag
1960          * and drop objects returned by the drag and drop events when we are
1961          * in INTERSECT mode.  It returns either the first object that the
1962          * cursor is over, or the object that has the greatest overlap with
1963          * the dragged element.
1964          * @method getBestMatch
1965          * @param  {DragDrop[]} dds The array of drag and drop objects
1966          * targeted
1967          * @return {DragDrop}       The best single match
1968          * @static
1969          */
1970         getBestMatch: function(dds) {
1971             var winner = null;
1972             // Return null if the input is not what we expect
1973             //if (!dds || !dds.length || dds.length == 0) {
1974                // winner = null;
1975             // If there is only one item, it wins
1976             //} else if (dds.length == 1) {
1977
1978             var len = dds.length;
1979
1980             if (len == 1) {
1981                 winner = dds[0];
1982             } else {
1983                 // Loop through the targeted items
1984                 for (var i=0; i<len; ++i) {
1985                     var dd = dds[i];
1986                     // If the cursor is over the object, it wins.  If the
1987                     // cursor is over multiple matches, the first one we come
1988                     // to wins.
1989                     if (dd.cursorIsOver) {
1990                         winner = dd;
1991                         break;
1992                     // Otherwise the object with the most overlap wins
1993                     } else {
1994                         if (!winner ||
1995                             winner.overlap.getArea() < dd.overlap.getArea()) {
1996                             winner = dd;
1997                         }
1998                     }
1999                 }
2000             }
2001
2002             return winner;
2003         },
2004
2005         /**
2006          * Refreshes the cache of the top-left and bottom-right points of the
2007          * drag and drop objects in the specified group(s).  This is in the
2008          * format that is stored in the drag and drop instance, so typical
2009          * usage is:
2010          * <code>
2011          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2012          * </code>
2013          * Alternatively:
2014          * <code>
2015          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2016          * </code>
2017          * @TODO this really should be an indexed array.  Alternatively this
2018          * method could accept both.
2019          * @method refreshCache
2020          * @param {Object} groups an associative array of groups to refresh
2021          * @static
2022          */
2023         refreshCache: function(groups) {
2024             for (var sGroup in groups) {
2025                 if ("string" != typeof sGroup) {
2026                     continue;
2027                 }
2028                 for (var i in this.ids[sGroup]) {
2029                     var oDD = this.ids[sGroup][i];
2030
2031                     if (this.isTypeOfDD(oDD)) {
2032                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2033                         var loc = this.getLocation(oDD);
2034                         if (loc) {
2035                             this.locationCache[oDD.id] = loc;
2036                         } else {
2037                             delete this.locationCache[oDD.id];
2038                             // this will unregister the drag and drop object if
2039                             // the element is not in a usable state
2040                             // oDD.unreg();
2041                         }
2042                     }
2043                 }
2044             }
2045         },
2046
2047         /**
2048          * This checks to make sure an element exists and is in the DOM.  The
2049          * main purpose is to handle cases where innerHTML is used to remove
2050          * drag and drop objects from the DOM.  IE provides an 'unspecified
2051          * error' when trying to access the offsetParent of such an element
2052          * @method verifyEl
2053          * @param {HTMLElement} el the element to check
2054          * @return {boolean} true if the element looks usable
2055          * @static
2056          */
2057         verifyEl: function(el) {
2058             if (el) {
2059                 var parent;
2060                 if(Roo.isIE){
2061                     try{
2062                         parent = el.offsetParent;
2063                     }catch(e){}
2064                 }else{
2065                     parent = el.offsetParent;
2066                 }
2067                 if (parent) {
2068                     return true;
2069                 }
2070             }
2071
2072             return false;
2073         },
2074
2075         /**
2076          * Returns a Region object containing the drag and drop element's position
2077          * and size, including the padding configured for it
2078          * @method getLocation
2079          * @param {DragDrop} oDD the drag and drop object to get the
2080          *                       location for
2081          * @return {Roo.lib.Region} a Region object representing the total area
2082          *                             the element occupies, including any padding
2083          *                             the instance is configured for.
2084          * @static
2085          */
2086         getLocation: function(oDD) {
2087             if (! this.isTypeOfDD(oDD)) {
2088                 return null;
2089             }
2090
2091             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2092
2093             try {
2094                 pos= Roo.lib.Dom.getXY(el);
2095             } catch (e) { }
2096
2097             if (!pos) {
2098                 return null;
2099             }
2100
2101             x1 = pos[0];
2102             x2 = x1 + el.offsetWidth;
2103             y1 = pos[1];
2104             y2 = y1 + el.offsetHeight;
2105
2106             t = y1 - oDD.padding[0];
2107             r = x2 + oDD.padding[1];
2108             b = y2 + oDD.padding[2];
2109             l = x1 - oDD.padding[3];
2110
2111             return new Roo.lib.Region( t, r, b, l );
2112         },
2113
2114         /**
2115          * Checks the cursor location to see if it over the target
2116          * @method isOverTarget
2117          * @param {Roo.lib.Point} pt The point to evaluate
2118          * @param {DragDrop} oTarget the DragDrop object we are inspecting
2119          * @return {boolean} true if the mouse is over the target
2120          * @private
2121          * @static
2122          */
2123         isOverTarget: function(pt, oTarget, intersect) {
2124             // use cache if available
2125             var loc = this.locationCache[oTarget.id];
2126             if (!loc || !this.useCache) {
2127                 loc = this.getLocation(oTarget);
2128                 this.locationCache[oTarget.id] = loc;
2129
2130             }
2131
2132             if (!loc) {
2133                 return false;
2134             }
2135
2136             oTarget.cursorIsOver = loc.contains( pt );
2137
2138             // DragDrop is using this as a sanity check for the initial mousedown
2139             // in this case we are done.  In POINT mode, if the drag obj has no
2140             // contraints, we are also done. Otherwise we need to evaluate the
2141             // location of the target as related to the actual location of the
2142             // dragged element.
2143             var dc = this.dragCurrent;
2144             if (!dc || !dc.getTargetCoord ||
2145                     (!intersect && !dc.constrainX && !dc.constrainY)) {
2146                 return oTarget.cursorIsOver;
2147             }
2148
2149             oTarget.overlap = null;
2150
2151             // Get the current location of the drag element, this is the
2152             // location of the mouse event less the delta that represents
2153             // where the original mousedown happened on the element.  We
2154             // need to consider constraints and ticks as well.
2155             var pos = dc.getTargetCoord(pt.x, pt.y);
2156
2157             var el = dc.getDragEl();
2158             var curRegion = new Roo.lib.Region( pos.y,
2159                                                    pos.x + el.offsetWidth,
2160                                                    pos.y + el.offsetHeight,
2161                                                    pos.x );
2162
2163             var overlap = curRegion.intersect(loc);
2164
2165             if (overlap) {
2166                 oTarget.overlap = overlap;
2167                 return (intersect) ? true : oTarget.cursorIsOver;
2168             } else {
2169                 return false;
2170             }
2171         },
2172
2173         /**
2174          * unload event handler
2175          * @method _onUnload
2176          * @private
2177          * @static
2178          */
2179         _onUnload: function(e, me) {
2180             Roo.dd.DragDropMgr.unregAll();
2181         },
2182
2183         /**
2184          * Cleans up the drag and drop events and objects.
2185          * @method unregAll
2186          * @private
2187          * @static
2188          */
2189         unregAll: function() {
2190
2191             if (this.dragCurrent) {
2192                 this.stopDrag();
2193                 this.dragCurrent = null;
2194             }
2195
2196             this._execOnAll("unreg", []);
2197
2198             for (i in this.elementCache) {
2199                 delete this.elementCache[i];
2200             }
2201
2202             this.elementCache = {};
2203             this.ids = {};
2204         },
2205
2206         /**
2207          * A cache of DOM elements
2208          * @property elementCache
2209          * @private
2210          * @static
2211          */
2212         elementCache: {},
2213
2214         /**
2215          * Get the wrapper for the DOM element specified
2216          * @method getElWrapper
2217          * @param {String} id the id of the element to get
2218          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2219          * @private
2220          * @deprecated This wrapper isn't that useful
2221          * @static
2222          */
2223         getElWrapper: function(id) {
2224             var oWrapper = this.elementCache[id];
2225             if (!oWrapper || !oWrapper.el) {
2226                 oWrapper = this.elementCache[id] =
2227                     new this.ElementWrapper(Roo.getDom(id));
2228             }
2229             return oWrapper;
2230         },
2231
2232         /**
2233          * Returns the actual DOM element
2234          * @method getElement
2235          * @param {String} id the id of the elment to get
2236          * @return {Object} The element
2237          * @deprecated use Roo.getDom instead
2238          * @static
2239          */
2240         getElement: function(id) {
2241             return Roo.getDom(id);
2242         },
2243
2244         /**
2245          * Returns the style property for the DOM element (i.e.,
2246          * document.getElById(id).style)
2247          * @method getCss
2248          * @param {String} id the id of the elment to get
2249          * @return {Object} The style property of the element
2250          * @deprecated use Roo.getDom instead
2251          * @static
2252          */
2253         getCss: function(id) {
2254             var el = Roo.getDom(id);
2255             return (el) ? el.style : null;
2256         },
2257
2258         /**
2259          * Inner class for cached elements
2260          * @class DragDropMgr.ElementWrapper
2261          * @for DragDropMgr
2262          * @private
2263          * @deprecated
2264          */
2265         ElementWrapper: function(el) {
2266                 /**
2267                  * The element
2268                  * @property el
2269                  */
2270                 this.el = el || null;
2271                 /**
2272                  * The element id
2273                  * @property id
2274                  */
2275                 this.id = this.el && el.id;
2276                 /**
2277                  * A reference to the style property
2278                  * @property css
2279                  */
2280                 this.css = this.el && el.style;
2281             },
2282
2283         /**
2284          * Returns the X position of an html element
2285          * @method getPosX
2286          * @param el the element for which to get the position
2287          * @return {int} the X coordinate
2288          * @for DragDropMgr
2289          * @deprecated use Roo.lib.Dom.getX instead
2290          * @static
2291          */
2292         getPosX: function(el) {
2293             return Roo.lib.Dom.getX(el);
2294         },
2295
2296         /**
2297          * Returns the Y position of an html element
2298          * @method getPosY
2299          * @param el the element for which to get the position
2300          * @return {int} the Y coordinate
2301          * @deprecated use Roo.lib.Dom.getY instead
2302          * @static
2303          */
2304         getPosY: function(el) {
2305             return Roo.lib.Dom.getY(el);
2306         },
2307
2308         /**
2309          * Swap two nodes.  In IE, we use the native method, for others we
2310          * emulate the IE behavior
2311          * @method swapNode
2312          * @param n1 the first node to swap
2313          * @param n2 the other node to swap
2314          * @static
2315          */
2316         swapNode: function(n1, n2) {
2317             if (n1.swapNode) {
2318                 n1.swapNode(n2);
2319             } else {
2320                 var p = n2.parentNode;
2321                 var s = n2.nextSibling;
2322
2323                 if (s == n1) {
2324                     p.insertBefore(n1, n2);
2325                 } else if (n2 == n1.nextSibling) {
2326                     p.insertBefore(n2, n1);
2327                 } else {
2328                     n1.parentNode.replaceChild(n2, n1);
2329                     p.insertBefore(n1, s);
2330                 }
2331             }
2332         },
2333
2334         /**
2335          * Returns the current scroll position
2336          * @method getScroll
2337          * @private
2338          * @static
2339          */
2340         getScroll: function () {
2341             var t, l, dde=document.documentElement, db=document.body;
2342             if (dde && (dde.scrollTop || dde.scrollLeft)) {
2343                 t = dde.scrollTop;
2344                 l = dde.scrollLeft;
2345             } else if (db) {
2346                 t = db.scrollTop;
2347                 l = db.scrollLeft;
2348             } else {
2349
2350             }
2351             return { top: t, left: l };
2352         },
2353
2354         /**
2355          * Returns the specified element style property
2356          * @method getStyle
2357          * @param {HTMLElement} el          the element
2358          * @param {string}      styleProp   the style property
2359          * @return {string} The value of the style property
2360          * @deprecated use Roo.lib.Dom.getStyle
2361          * @static
2362          */
2363         getStyle: function(el, styleProp) {
2364             return Roo.fly(el).getStyle(styleProp);
2365         },
2366
2367         /**
2368          * Gets the scrollTop
2369          * @method getScrollTop
2370          * @return {int} the document's scrollTop
2371          * @static
2372          */
2373         getScrollTop: function () { return this.getScroll().top; },
2374
2375         /**
2376          * Gets the scrollLeft
2377          * @method getScrollLeft
2378          * @return {int} the document's scrollTop
2379          * @static
2380          */
2381         getScrollLeft: function () { return this.getScroll().left; },
2382
2383         /**
2384          * Sets the x/y position of an element to the location of the
2385          * target element.
2386          * @method moveToEl
2387          * @param {HTMLElement} moveEl      The element to move
2388          * @param {HTMLElement} targetEl    The position reference element
2389          * @static
2390          */
2391         moveToEl: function (moveEl, targetEl) {
2392             var aCoord = Roo.lib.Dom.getXY(targetEl);
2393             Roo.lib.Dom.setXY(moveEl, aCoord);
2394         },
2395
2396         /**
2397          * Numeric array sort function
2398          * @method numericSort
2399          * @static
2400          */
2401         numericSort: function(a, b) { return (a - b); },
2402
2403         /**
2404          * Internal counter
2405          * @property _timeoutCount
2406          * @private
2407          * @static
2408          */
2409         _timeoutCount: 0,
2410
2411         /**
2412          * Trying to make the load order less important.  Without this we get
2413          * an error if this file is loaded before the Event Utility.
2414          * @method _addListeners
2415          * @private
2416          * @static
2417          */
2418         _addListeners: function() {
2419             var DDM = Roo.dd.DDM;
2420             if ( Roo.lib.Event && document ) {
2421                 DDM._onLoad();
2422             } else {
2423                 if (DDM._timeoutCount > 2000) {
2424                 } else {
2425                     setTimeout(DDM._addListeners, 10);
2426                     if (document && document.body) {
2427                         DDM._timeoutCount += 1;
2428                     }
2429                 }
2430             }
2431         },
2432
2433         /**
2434          * Recursively searches the immediate parent and all child nodes for
2435          * the handle element in order to determine wheter or not it was
2436          * clicked.
2437          * @method handleWasClicked
2438          * @param node the html element to inspect
2439          * @static
2440          */
2441         handleWasClicked: function(node, id) {
2442             if (this.isHandle(id, node.id)) {
2443                 return true;
2444             } else {
2445                 // check to see if this is a text node child of the one we want
2446                 var p = node.parentNode;
2447
2448                 while (p) {
2449                     if (this.isHandle(id, p.id)) {
2450                         return true;
2451                     } else {
2452                         p = p.parentNode;
2453                     }
2454                 }
2455             }
2456
2457             return false;
2458         }
2459
2460     };
2461
2462 }();
2463
2464 // shorter alias, save a few bytes
2465 Roo.dd.DDM = Roo.dd.DragDropMgr;
2466 Roo.dd.DDM._addListeners();
2467
2468 }/*
2469  * Based on:
2470  * Ext JS Library 1.1.1
2471  * Copyright(c) 2006-2007, Ext JS, LLC.
2472  *
2473  * Originally Released Under LGPL - original licence link has changed is not relivant.
2474  *
2475  * Fork - LGPL
2476  * <script type="text/javascript">
2477  */
2478
2479 /**
2480  * @class Roo.dd.DD
2481  * A DragDrop implementation where the linked element follows the
2482  * mouse cursor during a drag.
2483  * @extends Roo.dd.DragDrop
2484  * @constructor
2485  * @param {String} id the id of the linked element
2486  * @param {String} sGroup the group of related DragDrop items
2487  * @param {object} config an object containing configurable attributes
2488  *                Valid properties for DD:
2489  *                    scroll
2490  */
2491 Roo.dd.DD = function(id, sGroup, config) {
2492     if (id) {
2493         this.init(id, sGroup, config);
2494     }
2495 };
2496
2497 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2498
2499     /**
2500      * When set to true, the utility automatically tries to scroll the browser
2501      * window wehn a drag and drop element is dragged near the viewport boundary.
2502      * Defaults to true.
2503      * @property scroll
2504      * @type boolean
2505      */
2506     scroll: true,
2507
2508     /**
2509      * Sets the pointer offset to the distance between the linked element's top
2510      * left corner and the location the element was clicked
2511      * @method autoOffset
2512      * @param {int} iPageX the X coordinate of the click
2513      * @param {int} iPageY the Y coordinate of the click
2514      */
2515     autoOffset: function(iPageX, iPageY) {
2516         var x = iPageX - this.startPageX;
2517         var y = iPageY - this.startPageY;
2518         this.setDelta(x, y);
2519     },
2520
2521     /**
2522      * Sets the pointer offset.  You can call this directly to force the
2523      * offset to be in a particular location (e.g., pass in 0,0 to set it
2524      * to the center of the object)
2525      * @method setDelta
2526      * @param {int} iDeltaX the distance from the left
2527      * @param {int} iDeltaY the distance from the top
2528      */
2529     setDelta: function(iDeltaX, iDeltaY) {
2530         this.deltaX = iDeltaX;
2531         this.deltaY = iDeltaY;
2532     },
2533
2534     /**
2535      * Sets the drag element to the location of the mousedown or click event,
2536      * maintaining the cursor location relative to the location on the element
2537      * that was clicked.  Override this if you want to place the element in a
2538      * location other than where the cursor is.
2539      * @method setDragElPos
2540      * @param {int} iPageX the X coordinate of the mousedown or drag event
2541      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2542      */
2543     setDragElPos: function(iPageX, iPageY) {
2544         // the first time we do this, we are going to check to make sure
2545         // the element has css positioning
2546
2547         var el = this.getDragEl();
2548         this.alignElWithMouse(el, iPageX, iPageY);
2549     },
2550
2551     /**
2552      * Sets the element to the location of the mousedown or click event,
2553      * maintaining the cursor location relative to the location on the element
2554      * that was clicked.  Override this if you want to place the element in a
2555      * location other than where the cursor is.
2556      * @method alignElWithMouse
2557      * @param {HTMLElement} el the element to move
2558      * @param {int} iPageX the X coordinate of the mousedown or drag event
2559      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2560      */
2561     alignElWithMouse: function(el, iPageX, iPageY) {
2562         var oCoord = this.getTargetCoord(iPageX, iPageY);
2563         var fly = el.dom ? el : Roo.fly(el);
2564         if (!this.deltaSetXY) {
2565             var aCoord = [oCoord.x, oCoord.y];
2566             fly.setXY(aCoord);
2567             var newLeft = fly.getLeft(true);
2568             var newTop  = fly.getTop(true);
2569             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2570         } else {
2571             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2572         }
2573
2574         this.cachePosition(oCoord.x, oCoord.y);
2575         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2576         return oCoord;
2577     },
2578
2579     /**
2580      * Saves the most recent position so that we can reset the constraints and
2581      * tick marks on-demand.  We need to know this so that we can calculate the
2582      * number of pixels the element is offset from its original position.
2583      * @method cachePosition
2584      * @param iPageX the current x position (optional, this just makes it so we
2585      * don't have to look it up again)
2586      * @param iPageY the current y position (optional, this just makes it so we
2587      * don't have to look it up again)
2588      */
2589     cachePosition: function(iPageX, iPageY) {
2590         if (iPageX) {
2591             this.lastPageX = iPageX;
2592             this.lastPageY = iPageY;
2593         } else {
2594             var aCoord = Roo.lib.Dom.getXY(this.getEl());
2595             this.lastPageX = aCoord[0];
2596             this.lastPageY = aCoord[1];
2597         }
2598     },
2599
2600     /**
2601      * Auto-scroll the window if the dragged object has been moved beyond the
2602      * visible window boundary.
2603      * @method autoScroll
2604      * @param {int} x the drag element's x position
2605      * @param {int} y the drag element's y position
2606      * @param {int} h the height of the drag element
2607      * @param {int} w the width of the drag element
2608      * @private
2609      */
2610     autoScroll: function(x, y, h, w) {
2611
2612         if (this.scroll) {
2613             // The client height
2614             var clientH = Roo.lib.Dom.getViewWidth();
2615
2616             // The client width
2617             var clientW = Roo.lib.Dom.getViewHeight();
2618
2619             // The amt scrolled down
2620             var st = this.DDM.getScrollTop();
2621
2622             // The amt scrolled right
2623             var sl = this.DDM.getScrollLeft();
2624
2625             // Location of the bottom of the element
2626             var bot = h + y;
2627
2628             // Location of the right of the element
2629             var right = w + x;
2630
2631             // The distance from the cursor to the bottom of the visible area,
2632             // adjusted so that we don't scroll if the cursor is beyond the
2633             // element drag constraints
2634             var toBot = (clientH + st - y - this.deltaY);
2635
2636             // The distance from the cursor to the right of the visible area
2637             var toRight = (clientW + sl - x - this.deltaX);
2638
2639
2640             // How close to the edge the cursor must be before we scroll
2641             // var thresh = (document.all) ? 100 : 40;
2642             var thresh = 40;
2643
2644             // How many pixels to scroll per autoscroll op.  This helps to reduce
2645             // clunky scrolling. IE is more sensitive about this ... it needs this
2646             // value to be higher.
2647             var scrAmt = (document.all) ? 80 : 30;
2648
2649             // Scroll down if we are near the bottom of the visible page and the
2650             // obj extends below the crease
2651             if ( bot > clientH && toBot < thresh ) {
2652                 window.scrollTo(sl, st + scrAmt);
2653             }
2654
2655             // Scroll up if the window is scrolled down and the top of the object
2656             // goes above the top border
2657             if ( y < st && st > 0 && y - st < thresh ) {
2658                 window.scrollTo(sl, st - scrAmt);
2659             }
2660
2661             // Scroll right if the obj is beyond the right border and the cursor is
2662             // near the border.
2663             if ( right > clientW && toRight < thresh ) {
2664                 window.scrollTo(sl + scrAmt, st);
2665             }
2666
2667             // Scroll left if the window has been scrolled to the right and the obj
2668             // extends past the left border
2669             if ( x < sl && sl > 0 && x - sl < thresh ) {
2670                 window.scrollTo(sl - scrAmt, st);
2671             }
2672         }
2673     },
2674
2675     /**
2676      * Finds the location the element should be placed if we want to move
2677      * it to where the mouse location less the click offset would place us.
2678      * @method getTargetCoord
2679      * @param {int} iPageX the X coordinate of the click
2680      * @param {int} iPageY the Y coordinate of the click
2681      * @return an object that contains the coordinates (Object.x and Object.y)
2682      * @private
2683      */
2684     getTargetCoord: function(iPageX, iPageY) {
2685
2686
2687         var x = iPageX - this.deltaX;
2688         var y = iPageY - this.deltaY;
2689
2690         if (this.constrainX) {
2691             if (x < this.minX) { x = this.minX; }
2692             if (x > this.maxX) { x = this.maxX; }
2693         }
2694
2695         if (this.constrainY) {
2696             if (y < this.minY) { y = this.minY; }
2697             if (y > this.maxY) { y = this.maxY; }
2698         }
2699
2700         x = this.getTick(x, this.xTicks);
2701         y = this.getTick(y, this.yTicks);
2702
2703
2704         return {x:x, y:y};
2705     },
2706
2707     /*
2708      * Sets up config options specific to this class. Overrides
2709      * Roo.dd.DragDrop, but all versions of this method through the
2710      * inheritance chain are called
2711      */
2712     applyConfig: function() {
2713         Roo.dd.DD.superclass.applyConfig.call(this);
2714         this.scroll = (this.config.scroll !== false);
2715     },
2716
2717     /*
2718      * Event that fires prior to the onMouseDown event.  Overrides
2719      * Roo.dd.DragDrop.
2720      */
2721     b4MouseDown: function(e) {
2722         // this.resetConstraints();
2723         this.autoOffset(e.getPageX(),
2724                             e.getPageY());
2725     },
2726
2727     /*
2728      * Event that fires prior to the onDrag event.  Overrides
2729      * Roo.dd.DragDrop.
2730      */
2731     b4Drag: function(e) {
2732         this.setDragElPos(e.getPageX(),
2733                             e.getPageY());
2734     },
2735
2736     toString: function() {
2737         return ("DD " + this.id);
2738     }
2739
2740     //////////////////////////////////////////////////////////////////////////
2741     // Debugging ygDragDrop events that can be overridden
2742     //////////////////////////////////////////////////////////////////////////
2743     /*
2744     startDrag: function(x, y) {
2745     },
2746
2747     onDrag: function(e) {
2748     },
2749
2750     onDragEnter: function(e, id) {
2751     },
2752
2753     onDragOver: function(e, id) {
2754     },
2755
2756     onDragOut: function(e, id) {
2757     },
2758
2759     onDragDrop: function(e, id) {
2760     },
2761
2762     endDrag: function(e) {
2763     }
2764
2765     */
2766
2767 });/*
2768  * Based on:
2769  * Ext JS Library 1.1.1
2770  * Copyright(c) 2006-2007, Ext JS, LLC.
2771  *
2772  * Originally Released Under LGPL - original licence link has changed is not relivant.
2773  *
2774  * Fork - LGPL
2775  * <script type="text/javascript">
2776  */
2777
2778 /**
2779  * @class Roo.dd.DDProxy
2780  * A DragDrop implementation that inserts an empty, bordered div into
2781  * the document that follows the cursor during drag operations.  At the time of
2782  * the click, the frame div is resized to the dimensions of the linked html
2783  * element, and moved to the exact location of the linked element.
2784  *
2785  * References to the "frame" element refer to the single proxy element that
2786  * was created to be dragged in place of all DDProxy elements on the
2787  * page.
2788  *
2789  * @extends Roo.dd.DD
2790  * @constructor
2791  * @param {String} id the id of the linked html element
2792  * @param {String} sGroup the group of related DragDrop objects
2793  * @param {object} config an object containing configurable attributes
2794  *                Valid properties for DDProxy in addition to those in DragDrop:
2795  *                   resizeFrame, centerFrame, dragElId
2796  */
2797 Roo.dd.DDProxy = function(id, sGroup, config) {
2798     if (id) {
2799         this.init(id, sGroup, config);
2800         this.initFrame();
2801     }
2802 };
2803
2804 /**
2805  * The default drag frame div id
2806  * @property Roo.dd.DDProxy.dragElId
2807  * @type String
2808  * @static
2809  */
2810 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2811
2812 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2813
2814     /**
2815      * By default we resize the drag frame to be the same size as the element
2816      * we want to drag (this is to get the frame effect).  We can turn it off
2817      * if we want a different behavior.
2818      * @property resizeFrame
2819      * @type boolean
2820      */
2821     resizeFrame: true,
2822
2823     /**
2824      * By default the frame is positioned exactly where the drag element is, so
2825      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
2826      * you do not have constraints on the obj is to have the drag frame centered
2827      * around the cursor.  Set centerFrame to true for this effect.
2828      * @property centerFrame
2829      * @type boolean
2830      */
2831     centerFrame: false,
2832
2833     /**
2834      * Creates the proxy element if it does not yet exist
2835      * @method createFrame
2836      */
2837     createFrame: function() {
2838         var self = this;
2839         var body = document.body;
2840
2841         if (!body || !body.firstChild) {
2842             setTimeout( function() { self.createFrame(); }, 50 );
2843             return;
2844         }
2845
2846         var div = this.getDragEl();
2847
2848         if (!div) {
2849             div    = document.createElement("div");
2850             div.id = this.dragElId;
2851             var s  = div.style;
2852
2853             s.position   = "absolute";
2854             s.visibility = "hidden";
2855             s.cursor     = "move";
2856             s.border     = "2px solid #aaa";
2857             s.zIndex     = 999;
2858
2859             // appendChild can blow up IE if invoked prior to the window load event
2860             // while rendering a table.  It is possible there are other scenarios
2861             // that would cause this to happen as well.
2862             body.insertBefore(div, body.firstChild);
2863         }
2864     },
2865
2866     /**
2867      * Initialization for the drag frame element.  Must be called in the
2868      * constructor of all subclasses
2869      * @method initFrame
2870      */
2871     initFrame: function() {
2872         this.createFrame();
2873     },
2874
2875     applyConfig: function() {
2876         Roo.dd.DDProxy.superclass.applyConfig.call(this);
2877
2878         this.resizeFrame = (this.config.resizeFrame !== false);
2879         this.centerFrame = (this.config.centerFrame);
2880         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2881     },
2882
2883     /**
2884      * Resizes the drag frame to the dimensions of the clicked object, positions
2885      * it over the object, and finally displays it
2886      * @method showFrame
2887      * @param {int} iPageX X click position
2888      * @param {int} iPageY Y click position
2889      * @private
2890      */
2891     showFrame: function(iPageX, iPageY) {
2892         var el = this.getEl();
2893         var dragEl = this.getDragEl();
2894         var s = dragEl.style;
2895
2896         this._resizeProxy();
2897
2898         if (this.centerFrame) {
2899             this.setDelta( Math.round(parseInt(s.width,  10)/2),
2900                            Math.round(parseInt(s.height, 10)/2) );
2901         }
2902
2903         this.setDragElPos(iPageX, iPageY);
2904
2905         Roo.fly(dragEl).show();
2906     },
2907
2908     /**
2909      * The proxy is automatically resized to the dimensions of the linked
2910      * element when a drag is initiated, unless resizeFrame is set to false
2911      * @method _resizeProxy
2912      * @private
2913      */
2914     _resizeProxy: function() {
2915         if (this.resizeFrame) {
2916             var el = this.getEl();
2917             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2918         }
2919     },
2920
2921     // overrides Roo.dd.DragDrop
2922     b4MouseDown: function(e) {
2923         var x = e.getPageX();
2924         var y = e.getPageY();
2925         this.autoOffset(x, y);
2926         this.setDragElPos(x, y);
2927     },
2928
2929     // overrides Roo.dd.DragDrop
2930     b4StartDrag: function(x, y) {
2931         // show the drag frame
2932         this.showFrame(x, y);
2933     },
2934
2935     // overrides Roo.dd.DragDrop
2936     b4EndDrag: function(e) {
2937         Roo.fly(this.getDragEl()).hide();
2938     },
2939
2940     // overrides Roo.dd.DragDrop
2941     // By default we try to move the element to the last location of the frame.
2942     // This is so that the default behavior mirrors that of Roo.dd.DD.
2943     endDrag: function(e) {
2944
2945         var lel = this.getEl();
2946         var del = this.getDragEl();
2947
2948         // Show the drag frame briefly so we can get its position
2949         del.style.visibility = "";
2950
2951         this.beforeMove();
2952         // Hide the linked element before the move to get around a Safari
2953         // rendering bug.
2954         lel.style.visibility = "hidden";
2955         Roo.dd.DDM.moveToEl(lel, del);
2956         del.style.visibility = "hidden";
2957         lel.style.visibility = "";
2958
2959         this.afterDrag();
2960     },
2961
2962     beforeMove : function(){
2963
2964     },
2965
2966     afterDrag : function(){
2967
2968     },
2969
2970     toString: function() {
2971         return ("DDProxy " + this.id);
2972     }
2973
2974 });
2975 /*
2976  * Based on:
2977  * Ext JS Library 1.1.1
2978  * Copyright(c) 2006-2007, Ext JS, LLC.
2979  *
2980  * Originally Released Under LGPL - original licence link has changed is not relivant.
2981  *
2982  * Fork - LGPL
2983  * <script type="text/javascript">
2984  */
2985
2986  /**
2987  * @class Roo.dd.DDTarget
2988  * A DragDrop implementation that does not move, but can be a drop
2989  * target.  You would get the same result by simply omitting implementation
2990  * for the event callbacks, but this way we reduce the processing cost of the
2991  * event listener and the callbacks.
2992  * @extends Roo.dd.DragDrop
2993  * @constructor
2994  * @param {String} id the id of the element that is a drop target
2995  * @param {String} sGroup the group of related DragDrop objects
2996  * @param {object} config an object containing configurable attributes
2997  *                 Valid properties for DDTarget in addition to those in
2998  *                 DragDrop:
2999  *                    none
3000  */
3001 Roo.dd.DDTarget = function(id, sGroup, config) {
3002     if (id) {
3003         this.initTarget(id, sGroup, config);
3004     }
3005     if (config.listeners || config.events) { 
3006        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
3007             listeners : config.listeners || {}, 
3008             events : config.events || {} 
3009         });    
3010     }
3011 };
3012
3013 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3014 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3015     toString: function() {
3016         return ("DDTarget " + this.id);
3017     }
3018 });
3019 /*
3020  * Based on:
3021  * Ext JS Library 1.1.1
3022  * Copyright(c) 2006-2007, Ext JS, LLC.
3023  *
3024  * Originally Released Under LGPL - original licence link has changed is not relivant.
3025  *
3026  * Fork - LGPL
3027  * <script type="text/javascript">
3028  */
3029  
3030
3031 /**
3032  * @class Roo.dd.ScrollManager
3033  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3034  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3035  * @singleton
3036  */
3037 Roo.dd.ScrollManager = function(){
3038     var ddm = Roo.dd.DragDropMgr;
3039     var els = {};
3040     var dragEl = null;
3041     var proc = {};
3042     
3043     var onStop = function(e){
3044         dragEl = null;
3045         clearProc();
3046     };
3047     
3048     var triggerRefresh = function(){
3049         if(ddm.dragCurrent){
3050              ddm.refreshCache(ddm.dragCurrent.groups);
3051         }
3052     };
3053     
3054     var doScroll = function(){
3055         if(ddm.dragCurrent){
3056             var dds = Roo.dd.ScrollManager;
3057             if(!dds.animate){
3058                 if(proc.el.scroll(proc.dir, dds.increment)){
3059                     triggerRefresh();
3060                 }
3061             }else{
3062                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3063             }
3064         }
3065     };
3066     
3067     var clearProc = function(){
3068         if(proc.id){
3069             clearInterval(proc.id);
3070         }
3071         proc.id = 0;
3072         proc.el = null;
3073         proc.dir = "";
3074     };
3075     
3076     var startProc = function(el, dir){
3077         clearProc();
3078         proc.el = el;
3079         proc.dir = dir;
3080         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3081     };
3082     
3083     var onFire = function(e, isDrop){
3084         if(isDrop || !ddm.dragCurrent){ return; }
3085         var dds = Roo.dd.ScrollManager;
3086         if(!dragEl || dragEl != ddm.dragCurrent){
3087             dragEl = ddm.dragCurrent;
3088             // refresh regions on drag start
3089             dds.refreshCache();
3090         }
3091         
3092         var xy = Roo.lib.Event.getXY(e);
3093         var pt = new Roo.lib.Point(xy[0], xy[1]);
3094         for(var id in els){
3095             var el = els[id], r = el._region;
3096             if(r && r.contains(pt) && el.isScrollable()){
3097                 if(r.bottom - pt.y <= dds.thresh){
3098                     if(proc.el != el){
3099                         startProc(el, "down");
3100                     }
3101                     return;
3102                 }else if(r.right - pt.x <= dds.thresh){
3103                     if(proc.el != el){
3104                         startProc(el, "left");
3105                     }
3106                     return;
3107                 }else if(pt.y - r.top <= dds.thresh){
3108                     if(proc.el != el){
3109                         startProc(el, "up");
3110                     }
3111                     return;
3112                 }else if(pt.x - r.left <= dds.thresh){
3113                     if(proc.el != el){
3114                         startProc(el, "right");
3115                     }
3116                     return;
3117                 }
3118             }
3119         }
3120         clearProc();
3121     };
3122     
3123     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3124     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3125     
3126     return {
3127         /**
3128          * Registers new overflow element(s) to auto scroll
3129          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3130          */
3131         register : function(el){
3132             if(el instanceof Array){
3133                 for(var i = 0, len = el.length; i < len; i++) {
3134                         this.register(el[i]);
3135                 }
3136             }else{
3137                 el = Roo.get(el);
3138                 els[el.id] = el;
3139             }
3140         },
3141         
3142         /**
3143          * Unregisters overflow element(s) so they are no longer scrolled
3144          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3145          */
3146         unregister : function(el){
3147             if(el instanceof Array){
3148                 for(var i = 0, len = el.length; i < len; i++) {
3149                         this.unregister(el[i]);
3150                 }
3151             }else{
3152                 el = Roo.get(el);
3153                 delete els[el.id];
3154             }
3155         },
3156         
3157         /**
3158          * The number of pixels from the edge of a container the pointer needs to be to 
3159          * trigger scrolling (defaults to 25)
3160          * @type Number
3161          */
3162         thresh : 25,
3163         
3164         /**
3165          * The number of pixels to scroll in each scroll increment (defaults to 50)
3166          * @type Number
3167          */
3168         increment : 100,
3169         
3170         /**
3171          * The frequency of scrolls in milliseconds (defaults to 500)
3172          * @type Number
3173          */
3174         frequency : 500,
3175         
3176         /**
3177          * True to animate the scroll (defaults to true)
3178          * @type Boolean
3179          */
3180         animate: true,
3181         
3182         /**
3183          * The animation duration in seconds - 
3184          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3185          * @type Number
3186          */
3187         animDuration: .4,
3188         
3189         /**
3190          * Manually trigger a cache refresh.
3191          */
3192         refreshCache : function(){
3193             for(var id in els){
3194                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3195                     els[id]._region = els[id].getRegion();
3196                 }
3197             }
3198         }
3199     };
3200 }();/*
3201  * Based on:
3202  * Ext JS Library 1.1.1
3203  * Copyright(c) 2006-2007, Ext JS, LLC.
3204  *
3205  * Originally Released Under LGPL - original licence link has changed is not relivant.
3206  *
3207  * Fork - LGPL
3208  * <script type="text/javascript">
3209  */
3210  
3211
3212 /**
3213  * @class Roo.dd.Registry
3214  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3215  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3216  * @singleton
3217  */
3218 Roo.dd.Registry = function(){
3219     var elements = {}; 
3220     var handles = {}; 
3221     var autoIdSeed = 0;
3222
3223     var getId = function(el, autogen){
3224         if(typeof el == "string"){
3225             return el;
3226         }
3227         var id = el.id;
3228         if(!id && autogen !== false){
3229             id = "roodd-" + (++autoIdSeed);
3230             el.id = id;
3231         }
3232         return id;
3233     };
3234     
3235     return {
3236     /**
3237      * Register a drag drop element
3238      * @param {String|HTMLElement} element The id or DOM node to register
3239      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3240      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3241      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3242      * populated in the data object (if applicable):
3243      * <pre>
3244 Value      Description<br />
3245 ---------  ------------------------------------------<br />
3246 handles    Array of DOM nodes that trigger dragging<br />
3247            for the element being registered<br />
3248 isHandle   True if the element passed in triggers<br />
3249            dragging itself, else false
3250 </pre>
3251      */
3252         register : function(el, data){
3253             data = data || {};
3254             if(typeof el == "string"){
3255                 el = document.getElementById(el);
3256             }
3257             data.ddel = el;
3258             elements[getId(el)] = data;
3259             if(data.isHandle !== false){
3260                 handles[data.ddel.id] = data;
3261             }
3262             if(data.handles){
3263                 var hs = data.handles;
3264                 for(var i = 0, len = hs.length; i < len; i++){
3265                         handles[getId(hs[i])] = data;
3266                 }
3267             }
3268         },
3269
3270     /**
3271      * Unregister a drag drop element
3272      * @param {String|HTMLElement}  element The id or DOM node to unregister
3273      */
3274         unregister : function(el){
3275             var id = getId(el, false);
3276             var data = elements[id];
3277             if(data){
3278                 delete elements[id];
3279                 if(data.handles){
3280                     var hs = data.handles;
3281                     for(var i = 0, len = hs.length; i < len; i++){
3282                         delete handles[getId(hs[i], false)];
3283                     }
3284                 }
3285             }
3286         },
3287
3288     /**
3289      * Returns the handle registered for a DOM Node by id
3290      * @param {String|HTMLElement} id The DOM node or id to look up
3291      * @return {Object} handle The custom handle data
3292      */
3293         getHandle : function(id){
3294             if(typeof id != "string"){ // must be element?
3295                 id = id.id;
3296             }
3297             return handles[id];
3298         },
3299
3300     /**
3301      * Returns the handle that is registered for the DOM node that is the target of the event
3302      * @param {Event} e The event
3303      * @return {Object} handle The custom handle data
3304      */
3305         getHandleFromEvent : function(e){
3306             var t = Roo.lib.Event.getTarget(e);
3307             return t ? handles[t.id] : null;
3308         },
3309
3310     /**
3311      * Returns a custom data object that is registered for a DOM node by id
3312      * @param {String|HTMLElement} id The DOM node or id to look up
3313      * @return {Object} data The custom data
3314      */
3315         getTarget : function(id){
3316             if(typeof id != "string"){ // must be element?
3317                 id = id.id;
3318             }
3319             return elements[id];
3320         },
3321
3322     /**
3323      * Returns a custom data object that is registered for the DOM node that is the target of the event
3324      * @param {Event} e The event
3325      * @return {Object} data The custom data
3326      */
3327         getTargetFromEvent : function(e){
3328             var t = Roo.lib.Event.getTarget(e);
3329             return t ? elements[t.id] || handles[t.id] : null;
3330         }
3331     };
3332 }();/*
3333  * Based on:
3334  * Ext JS Library 1.1.1
3335  * Copyright(c) 2006-2007, Ext JS, LLC.
3336  *
3337  * Originally Released Under LGPL - original licence link has changed is not relivant.
3338  *
3339  * Fork - LGPL
3340  * <script type="text/javascript">
3341  */
3342  
3343
3344 /**
3345  * @class Roo.dd.StatusProxy
3346  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3347  * default drag proxy used by all Roo.dd components.
3348  * @constructor
3349  * @param {Object} config
3350  */
3351 Roo.dd.StatusProxy = function(config){
3352     Roo.apply(this, config);
3353     this.id = this.id || Roo.id();
3354     this.el = new Roo.Layer({
3355         dh: {
3356             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3357                 {tag: "div", cls: "x-dd-drop-icon"},
3358                 {tag: "div", cls: "x-dd-drag-ghost"}
3359             ]
3360         }, 
3361         shadow: !config || config.shadow !== false
3362     });
3363     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3364     this.dropStatus = this.dropNotAllowed;
3365 };
3366
3367 Roo.dd.StatusProxy.prototype = {
3368     /**
3369      * @cfg {String} dropAllowed
3370      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3371      */
3372     dropAllowed : "x-dd-drop-ok",
3373     /**
3374      * @cfg {String} dropNotAllowed
3375      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3376      */
3377     dropNotAllowed : "x-dd-drop-nodrop",
3378
3379     /**
3380      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3381      * over the current target element.
3382      * @param {String} cssClass The css class for the new drop status indicator image
3383      */
3384     setStatus : function(cssClass){
3385         cssClass = cssClass || this.dropNotAllowed;
3386         if(this.dropStatus != cssClass){
3387             this.el.replaceClass(this.dropStatus, cssClass);
3388             this.dropStatus = cssClass;
3389         }
3390     },
3391
3392     /**
3393      * Resets the status indicator to the default dropNotAllowed value
3394      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3395      */
3396     reset : function(clearGhost){
3397         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3398         this.dropStatus = this.dropNotAllowed;
3399         if(clearGhost){
3400             this.ghost.update("");
3401         }
3402     },
3403
3404     /**
3405      * Updates the contents of the ghost element
3406      * @param {String} html The html that will replace the current innerHTML of the ghost element
3407      */
3408     update : function(html){
3409         if(typeof html == "string"){
3410             this.ghost.update(html);
3411         }else{
3412             this.ghost.update("");
3413             html.style.margin = "0";
3414             this.ghost.dom.appendChild(html);
3415         }
3416         // ensure float = none set?? cant remember why though.
3417         var el = this.ghost.dom.firstChild;
3418                 if(el){
3419                         Roo.fly(el).setStyle('float', 'none');
3420                 }
3421     },
3422     
3423     /**
3424      * Returns the underlying proxy {@link Roo.Layer}
3425      * @return {Roo.Layer} el
3426     */
3427     getEl : function(){
3428         return this.el;
3429     },
3430
3431     /**
3432      * Returns the ghost element
3433      * @return {Roo.Element} el
3434      */
3435     getGhost : function(){
3436         return this.ghost;
3437     },
3438
3439     /**
3440      * Hides the proxy
3441      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3442      */
3443     hide : function(clear){
3444         this.el.hide();
3445         if(clear){
3446             this.reset(true);
3447         }
3448     },
3449
3450     /**
3451      * Stops the repair animation if it's currently running
3452      */
3453     stop : function(){
3454         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3455             this.anim.stop();
3456         }
3457     },
3458
3459     /**
3460      * Displays this proxy
3461      */
3462     show : function(){
3463         this.el.show();
3464     },
3465
3466     /**
3467      * Force the Layer to sync its shadow and shim positions to the element
3468      */
3469     sync : function(){
3470         this.el.sync();
3471     },
3472
3473     /**
3474      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3475      * invalid drop operation by the item being dragged.
3476      * @param {Array} xy The XY position of the element ([x, y])
3477      * @param {Function} callback The function to call after the repair is complete
3478      * @param {Object} scope The scope in which to execute the callback
3479      */
3480     repair : function(xy, callback, scope){
3481         this.callback = callback;
3482         this.scope = scope;
3483         if(xy && this.animRepair !== false){
3484             this.el.addClass("x-dd-drag-repair");
3485             this.el.hideUnders(true);
3486             this.anim = this.el.shift({
3487                 duration: this.repairDuration || .5,
3488                 easing: 'easeOut',
3489                 xy: xy,
3490                 stopFx: true,
3491                 callback: this.afterRepair,
3492                 scope: this
3493             });
3494         }else{
3495             this.afterRepair();
3496         }
3497     },
3498
3499     // private
3500     afterRepair : function(){
3501         this.hide(true);
3502         if(typeof this.callback == "function"){
3503             this.callback.call(this.scope || this);
3504         }
3505         this.callback = null;
3506         this.scope = null;
3507     }
3508 };/*
3509  * Based on:
3510  * Ext JS Library 1.1.1
3511  * Copyright(c) 2006-2007, Ext JS, LLC.
3512  *
3513  * Originally Released Under LGPL - original licence link has changed is not relivant.
3514  *
3515  * Fork - LGPL
3516  * <script type="text/javascript">
3517  */
3518
3519 /**
3520  * @class Roo.dd.DragSource
3521  * @extends Roo.dd.DDProxy
3522  * A simple class that provides the basic implementation needed to make any element draggable.
3523  * @constructor
3524  * @param {String/HTMLElement/Element} el The container element
3525  * @param {Object} config
3526  */
3527 Roo.dd.DragSource = function(el, config){
3528     this.el = Roo.get(el);
3529     this.dragData = {};
3530     
3531     Roo.apply(this, config);
3532     
3533     if(!this.proxy){
3534         this.proxy = new Roo.dd.StatusProxy();
3535     }
3536
3537     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3538           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3539     
3540     this.dragging = false;
3541 };
3542
3543 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3544     /**
3545      * @cfg {String} dropAllowed
3546      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3547      */
3548     dropAllowed : "x-dd-drop-ok",
3549     /**
3550      * @cfg {String} dropNotAllowed
3551      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3552      */
3553     dropNotAllowed : "x-dd-drop-nodrop",
3554
3555     /**
3556      * Returns the data object associated with this drag source
3557      * @return {Object} data An object containing arbitrary data
3558      */
3559     getDragData : function(e){
3560         return this.dragData;
3561     },
3562
3563     // private
3564     onDragEnter : function(e, id){
3565         var target = Roo.dd.DragDropMgr.getDDById(id);
3566         this.cachedTarget = target;
3567         if(this.beforeDragEnter(target, e, id) !== false){
3568             if(target.isNotifyTarget){
3569                 var status = target.notifyEnter(this, e, this.dragData);
3570                 this.proxy.setStatus(status);
3571             }else{
3572                 this.proxy.setStatus(this.dropAllowed);
3573             }
3574             
3575             if(this.afterDragEnter){
3576                 /**
3577                  * An empty function by default, but provided so that you can perform a custom action
3578                  * when the dragged item enters the drop target by providing an implementation.
3579                  * @param {Roo.dd.DragDrop} target The drop target
3580                  * @param {Event} e The event object
3581                  * @param {String} id The id of the dragged element
3582                  * @method afterDragEnter
3583                  */
3584                 this.afterDragEnter(target, e, id);
3585             }
3586         }
3587     },
3588
3589     /**
3590      * An empty function by default, but provided so that you can perform a custom action
3591      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3592      * @param {Roo.dd.DragDrop} target The drop target
3593      * @param {Event} e The event object
3594      * @param {String} id The id of the dragged element
3595      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3596      */
3597     beforeDragEnter : function(target, e, id){
3598         return true;
3599     },
3600
3601     // private
3602     alignElWithMouse: function() {
3603         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3604         this.proxy.sync();
3605     },
3606
3607     // private
3608     onDragOver : function(e, id){
3609         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3610         if(this.beforeDragOver(target, e, id) !== false){
3611             if(target.isNotifyTarget){
3612                 var status = target.notifyOver(this, e, this.dragData);
3613                 this.proxy.setStatus(status);
3614             }
3615
3616             if(this.afterDragOver){
3617                 /**
3618                  * An empty function by default, but provided so that you can perform a custom action
3619                  * while the dragged item is over the drop target by providing an implementation.
3620                  * @param {Roo.dd.DragDrop} target The drop target
3621                  * @param {Event} e The event object
3622                  * @param {String} id The id of the dragged element
3623                  * @method afterDragOver
3624                  */
3625                 this.afterDragOver(target, e, id);
3626             }
3627         }
3628     },
3629
3630     /**
3631      * An empty function by default, but provided so that you can perform a custom action
3632      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3633      * @param {Roo.dd.DragDrop} target The drop target
3634      * @param {Event} e The event object
3635      * @param {String} id The id of the dragged element
3636      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3637      */
3638     beforeDragOver : function(target, e, id){
3639         return true;
3640     },
3641
3642     // private
3643     onDragOut : function(e, id){
3644         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3645         if(this.beforeDragOut(target, e, id) !== false){
3646             if(target.isNotifyTarget){
3647                 target.notifyOut(this, e, this.dragData);
3648             }
3649             this.proxy.reset();
3650             if(this.afterDragOut){
3651                 /**
3652                  * An empty function by default, but provided so that you can perform a custom action
3653                  * after the dragged item is dragged out of the target without dropping.
3654                  * @param {Roo.dd.DragDrop} target The drop target
3655                  * @param {Event} e The event object
3656                  * @param {String} id The id of the dragged element
3657                  * @method afterDragOut
3658                  */
3659                 this.afterDragOut(target, e, id);
3660             }
3661         }
3662         this.cachedTarget = null;
3663     },
3664
3665     /**
3666      * An empty function by default, but provided so that you can perform a custom action before the dragged
3667      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3668      * @param {Roo.dd.DragDrop} target The drop target
3669      * @param {Event} e The event object
3670      * @param {String} id The id of the dragged element
3671      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3672      */
3673     beforeDragOut : function(target, e, id){
3674         return true;
3675     },
3676     
3677     // private
3678     onDragDrop : function(e, id){
3679         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3680         if(this.beforeDragDrop(target, e, id) !== false){
3681             if(target.isNotifyTarget){
3682                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3683                     this.onValidDrop(target, e, id);
3684                 }else{
3685                     this.onInvalidDrop(target, e, id);
3686                 }
3687             }else{
3688                 this.onValidDrop(target, e, id);
3689             }
3690             
3691             if(this.afterDragDrop){
3692                 /**
3693                  * An empty function by default, but provided so that you can perform a custom action
3694                  * after a valid drag drop has occurred by providing an implementation.
3695                  * @param {Roo.dd.DragDrop} target The drop target
3696                  * @param {Event} e The event object
3697                  * @param {String} id The id of the dropped element
3698                  * @method afterDragDrop
3699                  */
3700                 this.afterDragDrop(target, e, id);
3701             }
3702         }
3703         delete this.cachedTarget;
3704     },
3705
3706     /**
3707      * An empty function by default, but provided so that you can perform a custom action before the dragged
3708      * item is dropped onto the target and optionally cancel the onDragDrop.
3709      * @param {Roo.dd.DragDrop} target The drop target
3710      * @param {Event} e The event object
3711      * @param {String} id The id of the dragged element
3712      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3713      */
3714     beforeDragDrop : function(target, e, id){
3715         return true;
3716     },
3717
3718     // private
3719     onValidDrop : function(target, e, id){
3720         this.hideProxy();
3721         if(this.afterValidDrop){
3722             /**
3723              * An empty function by default, but provided so that you can perform a custom action
3724              * after a valid drop has occurred by providing an implementation.
3725              * @param {Object} target The target DD 
3726              * @param {Event} e The event object
3727              * @param {String} id The id of the dropped element
3728              * @method afterInvalidDrop
3729              */
3730             this.afterValidDrop(target, e, id);
3731         }
3732     },
3733
3734     // private
3735     getRepairXY : function(e, data){
3736         return this.el.getXY();  
3737     },
3738
3739     // private
3740     onInvalidDrop : function(target, e, id){
3741         this.beforeInvalidDrop(target, e, id);
3742         if(this.cachedTarget){
3743             if(this.cachedTarget.isNotifyTarget){
3744                 this.cachedTarget.notifyOut(this, e, this.dragData);
3745             }
3746             this.cacheTarget = null;
3747         }
3748         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3749
3750         if(this.afterInvalidDrop){
3751             /**
3752              * An empty function by default, but provided so that you can perform a custom action
3753              * after an invalid drop has occurred by providing an implementation.
3754              * @param {Event} e The event object
3755              * @param {String} id The id of the dropped element
3756              * @method afterInvalidDrop
3757              */
3758             this.afterInvalidDrop(e, id);
3759         }
3760     },
3761
3762     // private
3763     afterRepair : function(){
3764         if(Roo.enableFx){
3765             this.el.highlight(this.hlColor || "c3daf9");
3766         }
3767         this.dragging = false;
3768     },
3769
3770     /**
3771      * An empty function by default, but provided so that you can perform a custom action after an invalid
3772      * drop has occurred.
3773      * @param {Roo.dd.DragDrop} target The drop target
3774      * @param {Event} e The event object
3775      * @param {String} id The id of the dragged element
3776      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3777      */
3778     beforeInvalidDrop : function(target, e, id){
3779         return true;
3780     },
3781
3782     // private
3783     handleMouseDown : function(e){
3784         if(this.dragging) {
3785             return;
3786         }
3787         var data = this.getDragData(e);
3788         if(data && this.onBeforeDrag(data, e) !== false){
3789             this.dragData = data;
3790             this.proxy.stop();
3791             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3792         } 
3793     },
3794
3795     /**
3796      * An empty function by default, but provided so that you can perform a custom action before the initial
3797      * drag event begins and optionally cancel it.
3798      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3799      * @param {Event} e The event object
3800      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3801      */
3802     onBeforeDrag : function(data, e){
3803         return true;
3804     },
3805
3806     /**
3807      * An empty function by default, but provided so that you can perform a custom action once the initial
3808      * drag event has begun.  The drag cannot be canceled from this function.
3809      * @param {Number} x The x position of the click on the dragged object
3810      * @param {Number} y The y position of the click on the dragged object
3811      */
3812     onStartDrag : Roo.emptyFn,
3813
3814     // private - YUI override
3815     startDrag : function(x, y){
3816         this.proxy.reset();
3817         this.dragging = true;
3818         this.proxy.update("");
3819         this.onInitDrag(x, y);
3820         this.proxy.show();
3821     },
3822
3823     // private
3824     onInitDrag : function(x, y){
3825         var clone = this.el.dom.cloneNode(true);
3826         clone.id = Roo.id(); // prevent duplicate ids
3827         this.proxy.update(clone);
3828         this.onStartDrag(x, y);
3829         return true;
3830     },
3831
3832     /**
3833      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3834      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3835      */
3836     getProxy : function(){
3837         return this.proxy;  
3838     },
3839
3840     /**
3841      * Hides the drag source's {@link Roo.dd.StatusProxy}
3842      */
3843     hideProxy : function(){
3844         this.proxy.hide();  
3845         this.proxy.reset(true);
3846         this.dragging = false;
3847     },
3848
3849     // private
3850     triggerCacheRefresh : function(){
3851         Roo.dd.DDM.refreshCache(this.groups);
3852     },
3853
3854     // private - override to prevent hiding
3855     b4EndDrag: function(e) {
3856     },
3857
3858     // private - override to prevent moving
3859     endDrag : function(e){
3860         this.onEndDrag(this.dragData, e);
3861     },
3862
3863     // private
3864     onEndDrag : function(data, e){
3865     },
3866     
3867     // private - pin to cursor
3868     autoOffset : function(x, y) {
3869         this.setDelta(-12, -20);
3870     }    
3871 });/*
3872  * Based on:
3873  * Ext JS Library 1.1.1
3874  * Copyright(c) 2006-2007, Ext JS, LLC.
3875  *
3876  * Originally Released Under LGPL - original licence link has changed is not relivant.
3877  *
3878  * Fork - LGPL
3879  * <script type="text/javascript">
3880  */
3881
3882
3883 /**
3884  * @class Roo.dd.DropTarget
3885  * @extends Roo.dd.DDTarget
3886  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3887  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3888  * @constructor
3889  * @param {String/HTMLElement/Element} el The container element
3890  * @param {Object} config
3891  */
3892 Roo.dd.DropTarget = function(el, config){
3893     this.el = Roo.get(el);
3894     
3895     var listeners = false; ;
3896     if (config && config.listeners) {
3897         listeners= config.listeners;
3898         delete config.listeners;
3899     }
3900     Roo.apply(this, config);
3901     
3902     if(this.containerScroll){
3903         Roo.dd.ScrollManager.register(this.el);
3904     }
3905     this.addEvents( {
3906          /**
3907          * @scope Roo.dd.DropTarget
3908          */
3909          
3910          /**
3911          * @event enter
3912          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3913          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3914          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3915          * 
3916          * IMPORTANT : it should set this.overClass and this.dropAllowed
3917          * 
3918          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3919          * @param {Event} e The event
3920          * @param {Object} data An object containing arbitrary data supplied by the drag source
3921          */
3922         "enter" : true,
3923         
3924          /**
3925          * @event over
3926          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3927          * This method will be called on every mouse movement while the drag source is over the drop target.
3928          * This default implementation simply returns the dropAllowed config value.
3929          * 
3930          * IMPORTANT : it should set this.dropAllowed
3931          * 
3932          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3933          * @param {Event} e The event
3934          * @param {Object} data An object containing arbitrary data supplied by the drag source
3935          
3936          */
3937         "over" : true,
3938         /**
3939          * @event out
3940          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3941          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3942          * overClass (if any) from the drop element.
3943          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3944          * @param {Event} e The event
3945          * @param {Object} data An object containing arbitrary data supplied by the drag source
3946          */
3947          "out" : true,
3948          
3949         /**
3950          * @event drop
3951          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3952          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3953          * implementation that does something to process the drop event and returns true so that the drag source's
3954          * repair action does not run.
3955          * 
3956          * IMPORTANT : it should set this.success
3957          * 
3958          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3959          * @param {Event} e The event
3960          * @param {Object} data An object containing arbitrary data supplied by the drag source
3961         */
3962          "drop" : true
3963     });
3964             
3965      
3966     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3967         this.el.dom, 
3968         this.ddGroup || this.group,
3969         {
3970             isTarget: true,
3971             listeners : listeners || {} 
3972            
3973         
3974         }
3975     );
3976
3977 };
3978
3979 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
3980     /**
3981      * @cfg {String} overClass
3982      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
3983      */
3984      /**
3985      * @cfg {String} ddGroup
3986      * The drag drop group to handle drop events for
3987      */
3988      
3989     /**
3990      * @cfg {String} dropAllowed
3991      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3992      */
3993     dropAllowed : "x-dd-drop-ok",
3994     /**
3995      * @cfg {String} dropNotAllowed
3996      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3997      */
3998     dropNotAllowed : "x-dd-drop-nodrop",
3999     /**
4000      * @cfg {boolean} success
4001      * set this after drop listener.. 
4002      */
4003     success : false,
4004     /**
4005      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
4006      * if the drop point is valid for over/enter..
4007      */
4008     valid : false,
4009     // private
4010     isTarget : true,
4011
4012     // private
4013     isNotifyTarget : true,
4014     
4015     /**
4016      * @hide
4017      */
4018     notifyEnter : function(dd, e, data)
4019     {
4020         this.valid = true;
4021         this.fireEvent('enter', dd, e, data);
4022         if(this.overClass){
4023             this.el.addClass(this.overClass);
4024         }
4025         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4026             this.valid ? this.dropAllowed : this.dropNotAllowed
4027         );
4028     },
4029
4030     /**
4031      * @hide
4032      */
4033     notifyOver : function(dd, e, data)
4034     {
4035         this.valid = true;
4036         this.fireEvent('over', dd, e, data);
4037         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4038             this.valid ? this.dropAllowed : this.dropNotAllowed
4039         );
4040     },
4041
4042     /**
4043      * @hide
4044      */
4045     notifyOut : function(dd, e, data)
4046     {
4047         this.fireEvent('out', dd, e, data);
4048         if(this.overClass){
4049             this.el.removeClass(this.overClass);
4050         }
4051     },
4052
4053     /**
4054      * @hide
4055      */
4056     notifyDrop : function(dd, e, data)
4057     {
4058         this.success = false;
4059         this.fireEvent('drop', dd, e, data);
4060         return this.success;
4061     }
4062 });/*
4063  * Based on:
4064  * Ext JS Library 1.1.1
4065  * Copyright(c) 2006-2007, Ext JS, LLC.
4066  *
4067  * Originally Released Under LGPL - original licence link has changed is not relivant.
4068  *
4069  * Fork - LGPL
4070  * <script type="text/javascript">
4071  */
4072
4073
4074 /**
4075  * @class Roo.dd.DragZone
4076  * @extends Roo.dd.DragSource
4077  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4078  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4079  * @constructor
4080  * @param {String/HTMLElement/Element} el The container element
4081  * @param {Object} config
4082  */
4083 Roo.dd.DragZone = function(el, config){
4084     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4085     if(this.containerScroll){
4086         Roo.dd.ScrollManager.register(this.el);
4087     }
4088 };
4089
4090 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4091     /**
4092      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4093      * for auto scrolling during drag operations.
4094      */
4095     /**
4096      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4097      * method after a failed drop (defaults to "c3daf9" - light blue)
4098      */
4099
4100     /**
4101      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4102      * for a valid target to drag based on the mouse down. Override this method
4103      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4104      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4105      * @param {EventObject} e The mouse down event
4106      * @return {Object} The dragData
4107      */
4108     getDragData : function(e){
4109         return Roo.dd.Registry.getHandleFromEvent(e);
4110     },
4111     
4112     /**
4113      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4114      * this.dragData.ddel
4115      * @param {Number} x The x position of the click on the dragged object
4116      * @param {Number} y The y position of the click on the dragged object
4117      * @return {Boolean} true to continue the drag, false to cancel
4118      */
4119     onInitDrag : function(x, y){
4120         this.proxy.update(this.dragData.ddel.cloneNode(true));
4121         this.onStartDrag(x, y);
4122         return true;
4123     },
4124     
4125     /**
4126      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4127      */
4128     afterRepair : function(){
4129         if(Roo.enableFx){
4130             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4131         }
4132         this.dragging = false;
4133     },
4134
4135     /**
4136      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4137      * the XY of this.dragData.ddel
4138      * @param {EventObject} e The mouse up event
4139      * @return {Array} The xy location (e.g. [100, 200])
4140      */
4141     getRepairXY : function(e){
4142         return Roo.Element.fly(this.dragData.ddel).getXY();  
4143     }
4144 });/*
4145  * Based on:
4146  * Ext JS Library 1.1.1
4147  * Copyright(c) 2006-2007, Ext JS, LLC.
4148  *
4149  * Originally Released Under LGPL - original licence link has changed is not relivant.
4150  *
4151  * Fork - LGPL
4152  * <script type="text/javascript">
4153  */
4154 /**
4155  * @class Roo.dd.DropZone
4156  * @extends Roo.dd.DropTarget
4157  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4158  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4159  * @constructor
4160  * @param {String/HTMLElement/Element} el The container element
4161  * @param {Object} config
4162  */
4163 Roo.dd.DropZone = function(el, config){
4164     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4165 };
4166
4167 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4168     /**
4169      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4170      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4171      * provide your own custom lookup.
4172      * @param {Event} e The event
4173      * @return {Object} data The custom data
4174      */
4175     getTargetFromEvent : function(e){
4176         return Roo.dd.Registry.getTargetFromEvent(e);
4177     },
4178
4179     /**
4180      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4181      * that it has registered.  This method has no default implementation and should be overridden to provide
4182      * node-specific processing if necessary.
4183      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4184      * {@link #getTargetFromEvent} for this node)
4185      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4186      * @param {Event} e The event
4187      * @param {Object} data An object containing arbitrary data supplied by the drag source
4188      */
4189     onNodeEnter : function(n, dd, e, data){
4190         
4191     },
4192
4193     /**
4194      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4195      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4196      * overridden to provide the proper feedback.
4197      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4198      * {@link #getTargetFromEvent} for this node)
4199      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4200      * @param {Event} e The event
4201      * @param {Object} data An object containing arbitrary data supplied by the drag source
4202      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4203      * underlying {@link Roo.dd.StatusProxy} can be updated
4204      */
4205     onNodeOver : function(n, dd, e, data){
4206         return this.dropAllowed;
4207     },
4208
4209     /**
4210      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4211      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4212      * node-specific processing if necessary.
4213      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4214      * {@link #getTargetFromEvent} for this node)
4215      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4216      * @param {Event} e The event
4217      * @param {Object} data An object containing arbitrary data supplied by the drag source
4218      */
4219     onNodeOut : function(n, dd, e, data){
4220         
4221     },
4222
4223     /**
4224      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4225      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4226      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4227      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4228      * {@link #getTargetFromEvent} for this node)
4229      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4230      * @param {Event} e The event
4231      * @param {Object} data An object containing arbitrary data supplied by the drag source
4232      * @return {Boolean} True if the drop was valid, else false
4233      */
4234     onNodeDrop : function(n, dd, e, data){
4235         return false;
4236     },
4237
4238     /**
4239      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4240      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4241      * it should be overridden to provide the proper feedback if necessary.
4242      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4243      * @param {Event} e The event
4244      * @param {Object} data An object containing arbitrary data supplied by the drag source
4245      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4246      * underlying {@link Roo.dd.StatusProxy} can be updated
4247      */
4248     onContainerOver : function(dd, e, data){
4249         return this.dropNotAllowed;
4250     },
4251
4252     /**
4253      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4254      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4255      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4256      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4257      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4258      * @param {Event} e The event
4259      * @param {Object} data An object containing arbitrary data supplied by the drag source
4260      * @return {Boolean} True if the drop was valid, else false
4261      */
4262     onContainerDrop : function(dd, e, data){
4263         return false;
4264     },
4265
4266     /**
4267      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4268      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4269      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4270      * you should override this method and provide a custom implementation.
4271      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4272      * @param {Event} e The event
4273      * @param {Object} data An object containing arbitrary data supplied by the drag source
4274      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4275      * underlying {@link Roo.dd.StatusProxy} can be updated
4276      */
4277     notifyEnter : function(dd, e, data){
4278         return this.dropNotAllowed;
4279     },
4280
4281     /**
4282      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4283      * This method will be called on every mouse movement while the drag source is over the drop zone.
4284      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4285      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4286      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4287      * registered node, it will call {@link #onContainerOver}.
4288      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4289      * @param {Event} e The event
4290      * @param {Object} data An object containing arbitrary data supplied by the drag source
4291      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4292      * underlying {@link Roo.dd.StatusProxy} can be updated
4293      */
4294     notifyOver : function(dd, e, data){
4295         var n = this.getTargetFromEvent(e);
4296         if(!n){ // not over valid drop target
4297             if(this.lastOverNode){
4298                 this.onNodeOut(this.lastOverNode, dd, e, data);
4299                 this.lastOverNode = null;
4300             }
4301             return this.onContainerOver(dd, e, data);
4302         }
4303         if(this.lastOverNode != n){
4304             if(this.lastOverNode){
4305                 this.onNodeOut(this.lastOverNode, dd, e, data);
4306             }
4307             this.onNodeEnter(n, dd, e, data);
4308             this.lastOverNode = n;
4309         }
4310         return this.onNodeOver(n, dd, e, data);
4311     },
4312
4313     /**
4314      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4315      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4316      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4317      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4318      * @param {Event} e The event
4319      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4320      */
4321     notifyOut : function(dd, e, data){
4322         if(this.lastOverNode){
4323             this.onNodeOut(this.lastOverNode, dd, e, data);
4324             this.lastOverNode = null;
4325         }
4326     },
4327
4328     /**
4329      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4330      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4331      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4332      * otherwise it will call {@link #onContainerDrop}.
4333      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4334      * @param {Event} e The event
4335      * @param {Object} data An object containing arbitrary data supplied by the drag source
4336      * @return {Boolean} True if the drop was valid, else false
4337      */
4338     notifyDrop : function(dd, e, data){
4339         if(this.lastOverNode){
4340             this.onNodeOut(this.lastOverNode, dd, e, data);
4341             this.lastOverNode = null;
4342         }
4343         var n = this.getTargetFromEvent(e);
4344         return n ?
4345             this.onNodeDrop(n, dd, e, data) :
4346             this.onContainerDrop(dd, e, data);
4347     },
4348
4349     // private
4350     triggerCacheRefresh : function(){
4351         Roo.dd.DDM.refreshCache(this.groups);
4352     }  
4353 });/*
4354  * Based on:
4355  * Ext JS Library 1.1.1
4356  * Copyright(c) 2006-2007, Ext JS, LLC.
4357  *
4358  * Originally Released Under LGPL - original licence link has changed is not relivant.
4359  *
4360  * Fork - LGPL
4361  * <script type="text/javascript">
4362  */
4363
4364
4365 /**
4366  * @class Roo.data.SortTypes
4367  * @singleton
4368  * Defines the default sorting (casting?) comparison functions used when sorting data.
4369  */
4370 Roo.data.SortTypes = {
4371     /**
4372      * Default sort that does nothing
4373      * @param {Mixed} s The value being converted
4374      * @return {Mixed} The comparison value
4375      */
4376     none : function(s){
4377         return s;
4378     },
4379     
4380     /**
4381      * The regular expression used to strip tags
4382      * @type {RegExp}
4383      * @property
4384      */
4385     stripTagsRE : /<\/?[^>]+>/gi,
4386     
4387     /**
4388      * Strips all HTML tags to sort on text only
4389      * @param {Mixed} s The value being converted
4390      * @return {String} The comparison value
4391      */
4392     asText : function(s){
4393         return String(s).replace(this.stripTagsRE, "");
4394     },
4395     
4396     /**
4397      * Strips all HTML tags to sort on text only - Case insensitive
4398      * @param {Mixed} s The value being converted
4399      * @return {String} The comparison value
4400      */
4401     asUCText : function(s){
4402         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4403     },
4404     
4405     /**
4406      * Case insensitive string
4407      * @param {Mixed} s The value being converted
4408      * @return {String} The comparison value
4409      */
4410     asUCString : function(s) {
4411         return String(s).toUpperCase();
4412     },
4413     
4414     /**
4415      * Date sorting
4416      * @param {Mixed} s The value being converted
4417      * @return {Number} The comparison value
4418      */
4419     asDate : function(s) {
4420         if(!s){
4421             return 0;
4422         }
4423         if(s instanceof Date){
4424             return s.getTime();
4425         }
4426         return Date.parse(String(s));
4427     },
4428     
4429     /**
4430      * Float sorting
4431      * @param {Mixed} s The value being converted
4432      * @return {Float} The comparison value
4433      */
4434     asFloat : function(s) {
4435         var val = parseFloat(String(s).replace(/,/g, ""));
4436         if(isNaN(val)) val = 0;
4437         return val;
4438     },
4439     
4440     /**
4441      * Integer sorting
4442      * @param {Mixed} s The value being converted
4443      * @return {Number} The comparison value
4444      */
4445     asInt : function(s) {
4446         var val = parseInt(String(s).replace(/,/g, ""));
4447         if(isNaN(val)) val = 0;
4448         return val;
4449     }
4450 };/*
4451  * Based on:
4452  * Ext JS Library 1.1.1
4453  * Copyright(c) 2006-2007, Ext JS, LLC.
4454  *
4455  * Originally Released Under LGPL - original licence link has changed is not relivant.
4456  *
4457  * Fork - LGPL
4458  * <script type="text/javascript">
4459  */
4460
4461 /**
4462 * @class Roo.data.Record
4463  * Instances of this class encapsulate both record <em>definition</em> information, and record
4464  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4465  * to access Records cached in an {@link Roo.data.Store} object.<br>
4466  * <p>
4467  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4468  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4469  * objects.<br>
4470  * <p>
4471  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4472  * @constructor
4473  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4474  * {@link #create}. The parameters are the same.
4475  * @param {Array} data An associative Array of data values keyed by the field name.
4476  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4477  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4478  * not specified an integer id is generated.
4479  */
4480 Roo.data.Record = function(data, id){
4481     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4482     this.data = data;
4483 };
4484
4485 /**
4486  * Generate a constructor for a specific record layout.
4487  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4488  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4489  * Each field definition object may contain the following properties: <ul>
4490  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
4491  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4492  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4493  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4494  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4495  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4496  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4497  * this may be omitted.</p></li>
4498  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4499  * <ul><li>auto (Default, implies no conversion)</li>
4500  * <li>string</li>
4501  * <li>int</li>
4502  * <li>float</li>
4503  * <li>boolean</li>
4504  * <li>date</li></ul></p></li>
4505  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4506  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4507  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4508  * by the Reader into an object that will be stored in the Record. It is passed the
4509  * following parameters:<ul>
4510  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4511  * </ul></p></li>
4512  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4513  * </ul>
4514  * <br>usage:<br><pre><code>
4515 var TopicRecord = Roo.data.Record.create(
4516     {name: 'title', mapping: 'topic_title'},
4517     {name: 'author', mapping: 'username'},
4518     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4519     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4520     {name: 'lastPoster', mapping: 'user2'},
4521     {name: 'excerpt', mapping: 'post_text'}
4522 );
4523
4524 var myNewRecord = new TopicRecord({
4525     title: 'Do my job please',
4526     author: 'noobie',
4527     totalPosts: 1,
4528     lastPost: new Date(),
4529     lastPoster: 'Animal',
4530     excerpt: 'No way dude!'
4531 });
4532 myStore.add(myNewRecord);
4533 </code></pre>
4534  * @method create
4535  * @static
4536  */
4537 Roo.data.Record.create = function(o){
4538     var f = function(){
4539         f.superclass.constructor.apply(this, arguments);
4540     };
4541     Roo.extend(f, Roo.data.Record);
4542     var p = f.prototype;
4543     p.fields = new Roo.util.MixedCollection(false, function(field){
4544         return field.name;
4545     });
4546     for(var i = 0, len = o.length; i < len; i++){
4547         p.fields.add(new Roo.data.Field(o[i]));
4548     }
4549     f.getField = function(name){
4550         return p.fields.get(name);  
4551     };
4552     return f;
4553 };
4554
4555 Roo.data.Record.AUTO_ID = 1000;
4556 Roo.data.Record.EDIT = 'edit';
4557 Roo.data.Record.REJECT = 'reject';
4558 Roo.data.Record.COMMIT = 'commit';
4559
4560 Roo.data.Record.prototype = {
4561     /**
4562      * Readonly flag - true if this record has been modified.
4563      * @type Boolean
4564      */
4565     dirty : false,
4566     editing : false,
4567     error: null,
4568     modified: null,
4569
4570     // private
4571     join : function(store){
4572         this.store = store;
4573     },
4574
4575     /**
4576      * Set the named field to the specified value.
4577      * @param {String} name The name of the field to set.
4578      * @param {Object} value The value to set the field to.
4579      */
4580     set : function(name, value){
4581         if(this.data[name] == value){
4582             return;
4583         }
4584         this.dirty = true;
4585         if(!this.modified){
4586             this.modified = {};
4587         }
4588         if(typeof this.modified[name] == 'undefined'){
4589             this.modified[name] = this.data[name];
4590         }
4591         this.data[name] = value;
4592         if(!this.editing){
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     };
4725
4726     if(config && config.data){
4727         this.inlineData = config.data;
4728         delete config.data;
4729     }
4730
4731     Roo.apply(this, config);
4732     
4733     if(this.reader){ // reader passed
4734         this.reader = Roo.factory(this.reader, Roo.data);
4735         this.reader.xmodule = this.xmodule || false;
4736         if(!this.recordType){
4737             this.recordType = this.reader.recordType;
4738         }
4739         if(this.reader.onMetaChange){
4740             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4741         }
4742     }
4743
4744     if(this.recordType){
4745         this.fields = this.recordType.prototype.fields;
4746     }
4747     this.modified = [];
4748
4749     this.addEvents({
4750         /**
4751          * @event datachanged
4752          * Fires when the data cache has changed, and a widget which is using this Store
4753          * as a Record cache should refresh its view.
4754          * @param {Store} this
4755          */
4756         datachanged : true,
4757         /**
4758          * @event metachange
4759          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4760          * @param {Store} this
4761          * @param {Object} meta The JSON metadata
4762          */
4763         metachange : true,
4764         /**
4765          * @event add
4766          * Fires when Records have been added to the Store
4767          * @param {Store} this
4768          * @param {Roo.data.Record[]} records The array of Records added
4769          * @param {Number} index The index at which the record(s) were added
4770          */
4771         add : true,
4772         /**
4773          * @event remove
4774          * Fires when a Record has been removed from the Store
4775          * @param {Store} this
4776          * @param {Roo.data.Record} record The Record that was removed
4777          * @param {Number} index The index at which the record was removed
4778          */
4779         remove : true,
4780         /**
4781          * @event update
4782          * Fires when a Record has been updated
4783          * @param {Store} this
4784          * @param {Roo.data.Record} record The Record that was updated
4785          * @param {String} operation The update operation being performed.  Value may be one of:
4786          * <pre><code>
4787  Roo.data.Record.EDIT
4788  Roo.data.Record.REJECT
4789  Roo.data.Record.COMMIT
4790          * </code></pre>
4791          */
4792         update : true,
4793         /**
4794          * @event clear
4795          * Fires when the data cache has been cleared.
4796          * @param {Store} this
4797          */
4798         clear : true,
4799         /**
4800          * @event beforeload
4801          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4802          * the load action will be canceled.
4803          * @param {Store} this
4804          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4805          */
4806         beforeload : true,
4807         /**
4808          * @event load
4809          * Fires after a new set of Records has been loaded.
4810          * @param {Store} this
4811          * @param {Roo.data.Record[]} records The Records that were loaded
4812          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4813          */
4814         load : true,
4815         /**
4816          * @event loadexception
4817          * Fires if an exception occurs in the Proxy during loading.
4818          * Called with the signature of the Proxy's "loadexception" event.
4819          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4820          * 
4821          * @param {Proxy} 
4822          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4823          * @param {Object} load options 
4824          * @param {Object} jsonData from your request (normally this contains the Exception)
4825          */
4826         loadexception : true
4827     });
4828     
4829     if(this.proxy){
4830         this.proxy = Roo.factory(this.proxy, Roo.data);
4831         this.proxy.xmodule = this.xmodule || false;
4832         this.relayEvents(this.proxy,  ["loadexception"]);
4833     }
4834     this.sortToggle = {};
4835
4836     Roo.data.Store.superclass.constructor.call(this);
4837
4838     if(this.inlineData){
4839         this.loadData(this.inlineData);
4840         delete this.inlineData;
4841     }
4842 };
4843 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4844      /**
4845     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4846     * without a remote query - used by combo/forms at present.
4847     */
4848     
4849     /**
4850     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4851     */
4852     /**
4853     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4854     */
4855     /**
4856     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4857     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4858     */
4859     /**
4860     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4861     * on any HTTP request
4862     */
4863     /**
4864     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4865     */
4866     /**
4867     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4868     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4869     */
4870     remoteSort : false,
4871
4872     /**
4873     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4874      * loaded or when a record is removed. (defaults to false).
4875     */
4876     pruneModifiedRecords : false,
4877
4878     // private
4879     lastOptions : null,
4880
4881     /**
4882      * Add Records to the Store and fires the add event.
4883      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4884      */
4885     add : function(records){
4886         records = [].concat(records);
4887         for(var i = 0, len = records.length; i < len; i++){
4888             records[i].join(this);
4889         }
4890         var index = this.data.length;
4891         this.data.addAll(records);
4892         this.fireEvent("add", this, records, index);
4893     },
4894
4895     /**
4896      * Remove a Record from the Store and fires the remove event.
4897      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4898      */
4899     remove : function(record){
4900         var index = this.data.indexOf(record);
4901         this.data.removeAt(index);
4902         if(this.pruneModifiedRecords){
4903             this.modified.remove(record);
4904         }
4905         this.fireEvent("remove", this, record, index);
4906     },
4907
4908     /**
4909      * Remove all Records from the Store and fires the clear event.
4910      */
4911     removeAll : function(){
4912         this.data.clear();
4913         if(this.pruneModifiedRecords){
4914             this.modified = [];
4915         }
4916         this.fireEvent("clear", this);
4917     },
4918
4919     /**
4920      * Inserts Records to the Store at the given index and fires the add event.
4921      * @param {Number} index The start index at which to insert the passed Records.
4922      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4923      */
4924     insert : function(index, records){
4925         records = [].concat(records);
4926         for(var i = 0, len = records.length; i < len; i++){
4927             this.data.insert(index, records[i]);
4928             records[i].join(this);
4929         }
4930         this.fireEvent("add", this, records, index);
4931     },
4932
4933     /**
4934      * Get the index within the cache of the passed Record.
4935      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4936      * @return {Number} The index of the passed Record. Returns -1 if not found.
4937      */
4938     indexOf : function(record){
4939         return this.data.indexOf(record);
4940     },
4941
4942     /**
4943      * Get the index within the cache of the Record with the passed id.
4944      * @param {String} id The id of the Record to find.
4945      * @return {Number} The index of the Record. Returns -1 if not found.
4946      */
4947     indexOfId : function(id){
4948         return this.data.indexOfKey(id);
4949     },
4950
4951     /**
4952      * Get the Record with the specified id.
4953      * @param {String} id The id of the Record to find.
4954      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4955      */
4956     getById : function(id){
4957         return this.data.key(id);
4958     },
4959
4960     /**
4961      * Get the Record at the specified index.
4962      * @param {Number} index The index of the Record to find.
4963      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
4964      */
4965     getAt : function(index){
4966         return this.data.itemAt(index);
4967     },
4968
4969     /**
4970      * Returns a range of Records between specified indices.
4971      * @param {Number} startIndex (optional) The starting index (defaults to 0)
4972      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
4973      * @return {Roo.data.Record[]} An array of Records
4974      */
4975     getRange : function(start, end){
4976         return this.data.getRange(start, end);
4977     },
4978
4979     // private
4980     storeOptions : function(o){
4981         o = Roo.apply({}, o);
4982         delete o.callback;
4983         delete o.scope;
4984         this.lastOptions = o;
4985     },
4986
4987     /**
4988      * Loads the Record cache from the configured Proxy using the configured Reader.
4989      * <p>
4990      * If using remote paging, then the first load call must specify the <em>start</em>
4991      * and <em>limit</em> properties in the options.params property to establish the initial
4992      * position within the dataset, and the number of Records to cache on each read from the Proxy.
4993      * <p>
4994      * <strong>It is important to note that for remote data sources, loading is asynchronous,
4995      * and this call will return before the new data has been loaded. Perform any post-processing
4996      * in a callback function, or in a "load" event handler.</strong>
4997      * <p>
4998      * @param {Object} options An object containing properties which control loading options:<ul>
4999      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5000      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5001      * passed the following arguments:<ul>
5002      * <li>r : Roo.data.Record[]</li>
5003      * <li>options: Options object from the load call</li>
5004      * <li>success: Boolean success indicator</li></ul></li>
5005      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5006      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5007      * </ul>
5008      */
5009     load : function(options){
5010         options = options || {};
5011         if(this.fireEvent("beforeload", this, options) !== false){
5012             this.storeOptions(options);
5013             var p = Roo.apply(options.params || {}, this.baseParams);
5014             // if meta was not loaded from remote source.. try requesting it.
5015             if (!this.reader.metaFromRemote) {
5016                 p._requestMeta = 1;
5017             }
5018             if(this.sortInfo && this.remoteSort){
5019                 var pn = this.paramNames;
5020                 p[pn["sort"]] = this.sortInfo.field;
5021                 p[pn["dir"]] = this.sortInfo.direction;
5022             }
5023             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5024         }
5025     },
5026
5027     /**
5028      * Reloads the Record cache from the configured Proxy using the configured Reader and
5029      * the options from the last load operation performed.
5030      * @param {Object} options (optional) An object containing properties which may override the options
5031      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5032      * the most recently used options are reused).
5033      */
5034     reload : function(options){
5035         this.load(Roo.applyIf(options||{}, this.lastOptions));
5036     },
5037
5038     // private
5039     // Called as a callback by the Reader during a load operation.
5040     loadRecords : function(o, options, success){
5041         if(!o || success === false){
5042             if(success !== false){
5043                 this.fireEvent("load", this, [], options);
5044             }
5045             if(options.callback){
5046                 options.callback.call(options.scope || this, [], options, false);
5047             }
5048             return;
5049         }
5050         // if data returned failure - throw an exception.
5051         if (o.success === false) {
5052             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
5053             return;
5054         }
5055         var r = o.records, t = o.totalRecords || r.length;
5056         if(!options || options.add !== true){
5057             if(this.pruneModifiedRecords){
5058                 this.modified = [];
5059             }
5060             for(var i = 0, len = r.length; i < len; i++){
5061                 r[i].join(this);
5062             }
5063             if(this.snapshot){
5064                 this.data = this.snapshot;
5065                 delete this.snapshot;
5066             }
5067             this.data.clear();
5068             this.data.addAll(r);
5069             this.totalLength = t;
5070             this.applySort();
5071             this.fireEvent("datachanged", this);
5072         }else{
5073             this.totalLength = Math.max(t, this.data.length+r.length);
5074             this.add(r);
5075         }
5076         this.fireEvent("load", this, r, options);
5077         if(options.callback){
5078             options.callback.call(options.scope || this, r, options, true);
5079         }
5080     },
5081
5082     /**
5083      * Loads data from a passed data block. A Reader which understands the format of the data
5084      * must have been configured in the constructor.
5085      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5086      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5087      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5088      */
5089     loadData : function(o, append){
5090         var r = this.reader.readRecords(o);
5091         this.loadRecords(r, {add: append}, true);
5092     },
5093
5094     /**
5095      * Gets the number of cached records.
5096      * <p>
5097      * <em>If using paging, this may not be the total size of the dataset. If the data object
5098      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5099      * the data set size</em>
5100      */
5101     getCount : function(){
5102         return this.data.length || 0;
5103     },
5104
5105     /**
5106      * Gets the total number of records in the dataset as returned by the server.
5107      * <p>
5108      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5109      * the dataset size</em>
5110      */
5111     getTotalCount : function(){
5112         return this.totalLength || 0;
5113     },
5114
5115     /**
5116      * Returns the sort state of the Store as an object with two properties:
5117      * <pre><code>
5118  field {String} The name of the field by which the Records are sorted
5119  direction {String} The sort order, "ASC" or "DESC"
5120      * </code></pre>
5121      */
5122     getSortState : function(){
5123         return this.sortInfo;
5124     },
5125
5126     // private
5127     applySort : function(){
5128         if(this.sortInfo && !this.remoteSort){
5129             var s = this.sortInfo, f = s.field;
5130             var st = this.fields.get(f).sortType;
5131             var fn = function(r1, r2){
5132                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5133                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5134             };
5135             this.data.sort(s.direction, fn);
5136             if(this.snapshot && this.snapshot != this.data){
5137                 this.snapshot.sort(s.direction, fn);
5138             }
5139         }
5140     },
5141
5142     /**
5143      * Sets the default sort column and order to be used by the next load operation.
5144      * @param {String} fieldName The name of the field to sort by.
5145      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5146      */
5147     setDefaultSort : function(field, dir){
5148         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5149     },
5150
5151     /**
5152      * Sort the Records.
5153      * If remote sorting is used, the sort is performed on the server, and the cache is
5154      * reloaded. If local sorting is used, the cache is sorted internally.
5155      * @param {String} fieldName The name of the field to sort by.
5156      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5157      */
5158     sort : function(fieldName, dir){
5159         var f = this.fields.get(fieldName);
5160         if(!dir){
5161             if(this.sortInfo && this.sortInfo.field == f.name){ // toggle sort dir
5162                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5163             }else{
5164                 dir = f.sortDir;
5165             }
5166         }
5167         this.sortToggle[f.name] = dir;
5168         this.sortInfo = {field: f.name, direction: dir};
5169         if(!this.remoteSort){
5170             this.applySort();
5171             this.fireEvent("datachanged", this);
5172         }else{
5173             this.load(this.lastOptions);
5174         }
5175     },
5176
5177     /**
5178      * Calls the specified function for each of the Records in the cache.
5179      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5180      * Returning <em>false</em> aborts and exits the iteration.
5181      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5182      */
5183     each : function(fn, scope){
5184         this.data.each(fn, scope);
5185     },
5186
5187     /**
5188      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5189      * (e.g., during paging).
5190      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5191      */
5192     getModifiedRecords : function(){
5193         return this.modified;
5194     },
5195
5196     // private
5197     createFilterFn : function(property, value, anyMatch){
5198         if(!value.exec){ // not a regex
5199             value = String(value);
5200             if(value.length == 0){
5201                 return false;
5202             }
5203             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5204         }
5205         return function(r){
5206             return value.test(r.data[property]);
5207         };
5208     },
5209
5210     /**
5211      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5212      * @param {String} property A field on your records
5213      * @param {Number} start The record index to start at (defaults to 0)
5214      * @param {Number} end The last record index to include (defaults to length - 1)
5215      * @return {Number} The sum
5216      */
5217     sum : function(property, start, end){
5218         var rs = this.data.items, v = 0;
5219         start = start || 0;
5220         end = (end || end === 0) ? end : rs.length-1;
5221
5222         for(var i = start; i <= end; i++){
5223             v += (rs[i].data[property] || 0);
5224         }
5225         return v;
5226     },
5227
5228     /**
5229      * Filter the records by a specified property.
5230      * @param {String} field A field on your records
5231      * @param {String/RegExp} value Either a string that the field
5232      * should start with or a RegExp to test against the field
5233      * @param {Boolean} anyMatch True to match any part not just the beginning
5234      */
5235     filter : function(property, value, anyMatch){
5236         var fn = this.createFilterFn(property, value, anyMatch);
5237         return fn ? this.filterBy(fn) : this.clearFilter();
5238     },
5239
5240     /**
5241      * Filter by a function. The specified function will be called with each
5242      * record in this data source. If the function returns true the record is included,
5243      * otherwise it is filtered.
5244      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5245      * @param {Object} scope (optional) The scope of the function (defaults to this)
5246      */
5247     filterBy : function(fn, scope){
5248         this.snapshot = this.snapshot || this.data;
5249         this.data = this.queryBy(fn, scope||this);
5250         this.fireEvent("datachanged", this);
5251     },
5252
5253     /**
5254      * Query the records by a specified property.
5255      * @param {String} field A field on your records
5256      * @param {String/RegExp} value Either a string that the field
5257      * should start with or a RegExp to test against the field
5258      * @param {Boolean} anyMatch True to match any part not just the beginning
5259      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5260      */
5261     query : function(property, value, anyMatch){
5262         var fn = this.createFilterFn(property, value, anyMatch);
5263         return fn ? this.queryBy(fn) : this.data.clone();
5264     },
5265
5266     /**
5267      * Query by a function. The specified function will be called with each
5268      * record in this data source. If the function returns true the record is included
5269      * in the results.
5270      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5271      * @param {Object} scope (optional) The scope of the function (defaults to this)
5272       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5273      **/
5274     queryBy : function(fn, scope){
5275         var data = this.snapshot || this.data;
5276         return data.filterBy(fn, scope||this);
5277     },
5278
5279     /**
5280      * Collects unique values for a particular dataIndex from this store.
5281      * @param {String} dataIndex The property to collect
5282      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5283      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5284      * @return {Array} An array of the unique values
5285      **/
5286     collect : function(dataIndex, allowNull, bypassFilter){
5287         var d = (bypassFilter === true && this.snapshot) ?
5288                 this.snapshot.items : this.data.items;
5289         var v, sv, r = [], l = {};
5290         for(var i = 0, len = d.length; i < len; i++){
5291             v = d[i].data[dataIndex];
5292             sv = String(v);
5293             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5294                 l[sv] = true;
5295                 r[r.length] = v;
5296             }
5297         }
5298         return r;
5299     },
5300
5301     /**
5302      * Revert to a view of the Record cache with no filtering applied.
5303      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5304      */
5305     clearFilter : function(suppressEvent){
5306         if(this.snapshot && this.snapshot != this.data){
5307             this.data = this.snapshot;
5308             delete this.snapshot;
5309             if(suppressEvent !== true){
5310                 this.fireEvent("datachanged", this);
5311             }
5312         }
5313     },
5314
5315     // private
5316     afterEdit : function(record){
5317         if(this.modified.indexOf(record) == -1){
5318             this.modified.push(record);
5319         }
5320         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5321     },
5322
5323     // private
5324     afterReject : function(record){
5325         this.modified.remove(record);
5326         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5327     },
5328
5329     // private
5330     afterCommit : function(record){
5331         this.modified.remove(record);
5332         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5333     },
5334
5335     /**
5336      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5337      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5338      */
5339     commitChanges : function(){
5340         var m = this.modified.slice(0);
5341         this.modified = [];
5342         for(var i = 0, len = m.length; i < len; i++){
5343             m[i].commit();
5344         }
5345     },
5346
5347     /**
5348      * Cancel outstanding changes on all changed records.
5349      */
5350     rejectChanges : function(){
5351         var m = this.modified.slice(0);
5352         this.modified = [];
5353         for(var i = 0, len = m.length; i < len; i++){
5354             m[i].reject();
5355         }
5356     },
5357
5358     onMetaChange : function(meta, rtype, o){
5359         this.recordType = rtype;
5360         this.fields = rtype.prototype.fields;
5361         delete this.snapshot;
5362         this.sortInfo = meta.sortInfo || this.sortInfo;
5363         this.modified = [];
5364         this.fireEvent('metachange', this, this.reader.meta);
5365     }
5366 });/*
5367  * Based on:
5368  * Ext JS Library 1.1.1
5369  * Copyright(c) 2006-2007, Ext JS, LLC.
5370  *
5371  * Originally Released Under LGPL - original licence link has changed is not relivant.
5372  *
5373  * Fork - LGPL
5374  * <script type="text/javascript">
5375  */
5376
5377 /**
5378  * @class Roo.data.SimpleStore
5379  * @extends Roo.data.Store
5380  * Small helper class to make creating Stores from Array data easier.
5381  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5382  * @cfg {Array} fields An array of field definition objects, or field name strings.
5383  * @cfg {Array} data The multi-dimensional array of data
5384  * @constructor
5385  * @param {Object} config
5386  */
5387 Roo.data.SimpleStore = function(config){
5388     Roo.data.SimpleStore.superclass.constructor.call(this, {
5389         isLocal : true,
5390         reader: new Roo.data.ArrayReader({
5391                 id: config.id
5392             },
5393             Roo.data.Record.create(config.fields)
5394         ),
5395         proxy : new Roo.data.MemoryProxy(config.data)
5396     });
5397     this.load();
5398 };
5399 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5400  * Based on:
5401  * Ext JS Library 1.1.1
5402  * Copyright(c) 2006-2007, Ext JS, LLC.
5403  *
5404  * Originally Released Under LGPL - original licence link has changed is not relivant.
5405  *
5406  * Fork - LGPL
5407  * <script type="text/javascript">
5408  */
5409
5410 /**
5411 /**
5412  * @extends Roo.data.Store
5413  * @class Roo.data.JsonStore
5414  * Small helper class to make creating Stores for JSON data easier. <br/>
5415 <pre><code>
5416 var store = new Roo.data.JsonStore({
5417     url: 'get-images.php',
5418     root: 'images',
5419     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5420 });
5421 </code></pre>
5422  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5423  * JsonReader and HttpProxy (unless inline data is provided).</b>
5424  * @cfg {Array} fields An array of field definition objects, or field name strings.
5425  * @constructor
5426  * @param {Object} config
5427  */
5428 Roo.data.JsonStore = function(c){
5429     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5430         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5431         reader: new Roo.data.JsonReader(c, c.fields)
5432     }));
5433 };
5434 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5435  * Based on:
5436  * Ext JS Library 1.1.1
5437  * Copyright(c) 2006-2007, Ext JS, LLC.
5438  *
5439  * Originally Released Under LGPL - original licence link has changed is not relivant.
5440  *
5441  * Fork - LGPL
5442  * <script type="text/javascript">
5443  */
5444
5445  
5446 Roo.data.Field = function(config){
5447     if(typeof config == "string"){
5448         config = {name: config};
5449     }
5450     Roo.apply(this, config);
5451     
5452     if(!this.type){
5453         this.type = "auto";
5454     }
5455     
5456     var st = Roo.data.SortTypes;
5457     // named sortTypes are supported, here we look them up
5458     if(typeof this.sortType == "string"){
5459         this.sortType = st[this.sortType];
5460     }
5461     
5462     // set default sortType for strings and dates
5463     if(!this.sortType){
5464         switch(this.type){
5465             case "string":
5466                 this.sortType = st.asUCString;
5467                 break;
5468             case "date":
5469                 this.sortType = st.asDate;
5470                 break;
5471             default:
5472                 this.sortType = st.none;
5473         }
5474     }
5475
5476     // define once
5477     var stripRe = /[\$,%]/g;
5478
5479     // prebuilt conversion function for this field, instead of
5480     // switching every time we're reading a value
5481     if(!this.convert){
5482         var cv, dateFormat = this.dateFormat;
5483         switch(this.type){
5484             case "":
5485             case "auto":
5486             case undefined:
5487                 cv = function(v){ return v; };
5488                 break;
5489             case "string":
5490                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5491                 break;
5492             case "int":
5493                 cv = function(v){
5494                     return v !== undefined && v !== null && v !== '' ?
5495                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5496                     };
5497                 break;
5498             case "float":
5499                 cv = function(v){
5500                     return v !== undefined && v !== null && v !== '' ?
5501                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5502                     };
5503                 break;
5504             case "bool":
5505             case "boolean":
5506                 cv = function(v){ return v === true || v === "true" || v == 1; };
5507                 break;
5508             case "date":
5509                 cv = function(v){
5510                     if(!v){
5511                         return '';
5512                     }
5513                     if(v instanceof Date){
5514                         return v;
5515                     }
5516                     if(dateFormat){
5517                         if(dateFormat == "timestamp"){
5518                             return new Date(v*1000);
5519                         }
5520                         return Date.parseDate(v, dateFormat);
5521                     }
5522                     var parsed = Date.parse(v);
5523                     return parsed ? new Date(parsed) : null;
5524                 };
5525              break;
5526             
5527         }
5528         this.convert = cv;
5529     }
5530 };
5531
5532 Roo.data.Field.prototype = {
5533     dateFormat: null,
5534     defaultValue: "",
5535     mapping: null,
5536     sortType : null,
5537     sortDir : "ASC"
5538 };/*
5539  * Based on:
5540  * Ext JS Library 1.1.1
5541  * Copyright(c) 2006-2007, Ext JS, LLC.
5542  *
5543  * Originally Released Under LGPL - original licence link has changed is not relivant.
5544  *
5545  * Fork - LGPL
5546  * <script type="text/javascript">
5547  */
5548  
5549 // Base class for reading structured data from a data source.  This class is intended to be
5550 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5551
5552 /**
5553  * @class Roo.data.DataReader
5554  * Base class for reading structured data from a data source.  This class is intended to be
5555  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5556  */
5557
5558 Roo.data.DataReader = function(meta, recordType){
5559     
5560     this.meta = meta;
5561     
5562     this.recordType = recordType instanceof Array ? 
5563         Roo.data.Record.create(recordType) : recordType;
5564 };
5565
5566 Roo.data.DataReader.prototype = {
5567      /**
5568      * Create an empty record
5569      * @param {Object} data (optional) - overlay some values
5570      * @return {Roo.data.Record} record created.
5571      */
5572     newRow :  function(d) {
5573         var da =  {};
5574         this.recordType.prototype.fields.each(function(c) {
5575             switch( c.type) {
5576                 case 'int' : da[c.name] = 0; break;
5577                 case 'date' : da[c.name] = new Date(); break;
5578                 case 'float' : da[c.name] = 0.0; break;
5579                 case 'boolean' : da[c.name] = false; break;
5580                 default : da[c.name] = ""; break;
5581             }
5582             
5583         });
5584         return new this.recordType(Roo.apply(da, d));
5585     }
5586     
5587 };/*
5588  * Based on:
5589  * Ext JS Library 1.1.1
5590  * Copyright(c) 2006-2007, Ext JS, LLC.
5591  *
5592  * Originally Released Under LGPL - original licence link has changed is not relivant.
5593  *
5594  * Fork - LGPL
5595  * <script type="text/javascript">
5596  */
5597
5598 /**
5599  * @class Roo.data.DataProxy
5600  * @extends Roo.data.Observable
5601  * This class is an abstract base class for implementations which provide retrieval of
5602  * unformatted data objects.<br>
5603  * <p>
5604  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5605  * (of the appropriate type which knows how to parse the data object) to provide a block of
5606  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5607  * <p>
5608  * Custom implementations must implement the load method as described in
5609  * {@link Roo.data.HttpProxy#load}.
5610  */
5611 Roo.data.DataProxy = function(){
5612     this.addEvents({
5613         /**
5614          * @event beforeload
5615          * Fires before a network request is made to retrieve a data object.
5616          * @param {Object} This DataProxy object.
5617          * @param {Object} params The params parameter to the load function.
5618          */
5619         beforeload : true,
5620         /**
5621          * @event load
5622          * Fires before the load method's callback is called.
5623          * @param {Object} This DataProxy object.
5624          * @param {Object} o The data object.
5625          * @param {Object} arg The callback argument object passed to the load function.
5626          */
5627         load : true,
5628         /**
5629          * @event loadexception
5630          * Fires if an Exception occurs during data retrieval.
5631          * @param {Object} This DataProxy object.
5632          * @param {Object} o The data object.
5633          * @param {Object} arg The callback argument object passed to the load function.
5634          * @param {Object} e The Exception.
5635          */
5636         loadexception : true
5637     });
5638     Roo.data.DataProxy.superclass.constructor.call(this);
5639 };
5640
5641 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5642
5643     /**
5644      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5645      */
5646 /*
5647  * Based on:
5648  * Ext JS Library 1.1.1
5649  * Copyright(c) 2006-2007, Ext JS, LLC.
5650  *
5651  * Originally Released Under LGPL - original licence link has changed is not relivant.
5652  *
5653  * Fork - LGPL
5654  * <script type="text/javascript">
5655  */
5656 /**
5657  * @class Roo.data.MemoryProxy
5658  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5659  * to the Reader when its load method is called.
5660  * @constructor
5661  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5662  */
5663 Roo.data.MemoryProxy = function(data){
5664     if (data.data) {
5665         data = data.data;
5666     }
5667     Roo.data.MemoryProxy.superclass.constructor.call(this);
5668     this.data = data;
5669 };
5670
5671 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5672     /**
5673      * Load data from the requested source (in this case an in-memory
5674      * data object passed to the constructor), read the data object into
5675      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5676      * process that block using the passed callback.
5677      * @param {Object} params This parameter is not used by the MemoryProxy class.
5678      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5679      * object into a block of Roo.data.Records.
5680      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5681      * The function must be passed <ul>
5682      * <li>The Record block object</li>
5683      * <li>The "arg" argument from the load function</li>
5684      * <li>A boolean success indicator</li>
5685      * </ul>
5686      * @param {Object} scope The scope in which to call the callback
5687      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5688      */
5689     load : function(params, reader, callback, scope, arg){
5690         params = params || {};
5691         var result;
5692         try {
5693             result = reader.readRecords(this.data);
5694         }catch(e){
5695             this.fireEvent("loadexception", this, arg, null, e);
5696             callback.call(scope, null, arg, false);
5697             return;
5698         }
5699         callback.call(scope, result, arg, true);
5700     },
5701     
5702     // private
5703     update : function(params, records){
5704         
5705     }
5706 });/*
5707  * Based on:
5708  * Ext JS Library 1.1.1
5709  * Copyright(c) 2006-2007, Ext JS, LLC.
5710  *
5711  * Originally Released Under LGPL - original licence link has changed is not relivant.
5712  *
5713  * Fork - LGPL
5714  * <script type="text/javascript">
5715  */
5716 /**
5717  * @class Roo.data.HttpProxy
5718  * @extends Roo.data.DataProxy
5719  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5720  * configured to reference a certain URL.<br><br>
5721  * <p>
5722  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5723  * from which the running page was served.<br><br>
5724  * <p>
5725  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5726  * <p>
5727  * Be aware that to enable the browser to parse an XML document, the server must set
5728  * the Content-Type header in the HTTP response to "text/xml".
5729  * @constructor
5730  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5731  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5732  * will be used to make the request.
5733  */
5734 Roo.data.HttpProxy = function(conn){
5735     Roo.data.HttpProxy.superclass.constructor.call(this);
5736     // is conn a conn config or a real conn?
5737     this.conn = conn;
5738     this.useAjax = !conn || !conn.events;
5739   
5740 };
5741
5742 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5743     // thse are take from connection...
5744     
5745     /**
5746      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5747      */
5748     /**
5749      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5750      * extra parameters to each request made by this object. (defaults to undefined)
5751      */
5752     /**
5753      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5754      *  to each request made by this object. (defaults to undefined)
5755      */
5756     /**
5757      * @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)
5758      */
5759     /**
5760      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5761      */
5762      /**
5763      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5764      * @type Boolean
5765      */
5766   
5767
5768     /**
5769      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5770      * @type Boolean
5771      */
5772     /**
5773      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5774      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5775      * a finer-grained basis than the DataProxy events.
5776      */
5777     getConnection : function(){
5778         return this.useAjax ? Roo.Ajax : this.conn;
5779     },
5780
5781     /**
5782      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5783      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5784      * process that block using the passed callback.
5785      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5786      * for the request to the remote server.
5787      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5788      * object into a block of Roo.data.Records.
5789      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5790      * The function must be passed <ul>
5791      * <li>The Record block object</li>
5792      * <li>The "arg" argument from the load function</li>
5793      * <li>A boolean success indicator</li>
5794      * </ul>
5795      * @param {Object} scope The scope in which to call the callback
5796      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5797      */
5798     load : function(params, reader, callback, scope, arg){
5799         if(this.fireEvent("beforeload", this, params) !== false){
5800             var  o = {
5801                 params : params || {},
5802                 request: {
5803                     callback : callback,
5804                     scope : scope,
5805                     arg : arg
5806                 },
5807                 reader: reader,
5808                 callback : this.loadResponse,
5809                 scope: this
5810             };
5811             if(this.useAjax){
5812                 Roo.applyIf(o, this.conn);
5813                 if(this.activeRequest){
5814                     Roo.Ajax.abort(this.activeRequest);
5815                 }
5816                 this.activeRequest = Roo.Ajax.request(o);
5817             }else{
5818                 this.conn.request(o);
5819             }
5820         }else{
5821             callback.call(scope||this, null, arg, false);
5822         }
5823     },
5824
5825     // private
5826     loadResponse : function(o, success, response){
5827         delete this.activeRequest;
5828         if(!success){
5829             this.fireEvent("loadexception", this, o, response);
5830             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5831             return;
5832         }
5833         var result;
5834         try {
5835             result = o.reader.read(response);
5836         }catch(e){
5837             this.fireEvent("loadexception", this, o, response, e);
5838             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5839             return;
5840         }
5841         
5842         this.fireEvent("load", this, o, o.request.arg);
5843         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5844     },
5845
5846     // private
5847     update : function(dataSet){
5848
5849     },
5850
5851     // private
5852     updateResponse : function(dataSet){
5853
5854     }
5855 });/*
5856  * Based on:
5857  * Ext JS Library 1.1.1
5858  * Copyright(c) 2006-2007, Ext JS, LLC.
5859  *
5860  * Originally Released Under LGPL - original licence link has changed is not relivant.
5861  *
5862  * Fork - LGPL
5863  * <script type="text/javascript">
5864  */
5865
5866 /**
5867  * @class Roo.data.ScriptTagProxy
5868  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5869  * other than the originating domain of the running page.<br><br>
5870  * <p>
5871  * <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
5872  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5873  * <p>
5874  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5875  * source code that is used as the source inside a &lt;script> tag.<br><br>
5876  * <p>
5877  * In order for the browser to process the returned data, the server must wrap the data object
5878  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5879  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5880  * depending on whether the callback name was passed:
5881  * <p>
5882  * <pre><code>
5883 boolean scriptTag = false;
5884 String cb = request.getParameter("callback");
5885 if (cb != null) {
5886     scriptTag = true;
5887     response.setContentType("text/javascript");
5888 } else {
5889     response.setContentType("application/x-json");
5890 }
5891 Writer out = response.getWriter();
5892 if (scriptTag) {
5893     out.write(cb + "(");
5894 }
5895 out.print(dataBlock.toJsonString());
5896 if (scriptTag) {
5897     out.write(");");
5898 }
5899 </pre></code>
5900  *
5901  * @constructor
5902  * @param {Object} config A configuration object.
5903  */
5904 Roo.data.ScriptTagProxy = function(config){
5905     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5906     Roo.apply(this, config);
5907     this.head = document.getElementsByTagName("head")[0];
5908 };
5909
5910 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5911
5912 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5913     /**
5914      * @cfg {String} url The URL from which to request the data object.
5915      */
5916     /**
5917      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5918      */
5919     timeout : 30000,
5920     /**
5921      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5922      * the server the name of the callback function set up by the load call to process the returned data object.
5923      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5924      * javascript output which calls this named function passing the data object as its only parameter.
5925      */
5926     callbackParam : "callback",
5927     /**
5928      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5929      * name to the request.
5930      */
5931     nocache : true,
5932
5933     /**
5934      * Load data from the configured URL, read the data object into
5935      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5936      * process that block using the passed callback.
5937      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5938      * for the request to the remote server.
5939      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5940      * object into a block of Roo.data.Records.
5941      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5942      * The function must be passed <ul>
5943      * <li>The Record block object</li>
5944      * <li>The "arg" argument from the load function</li>
5945      * <li>A boolean success indicator</li>
5946      * </ul>
5947      * @param {Object} scope The scope in which to call the callback
5948      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5949      */
5950     load : function(params, reader, callback, scope, arg){
5951         if(this.fireEvent("beforeload", this, params) !== false){
5952
5953             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
5954
5955             var url = this.url;
5956             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
5957             if(this.nocache){
5958                 url += "&_dc=" + (new Date().getTime());
5959             }
5960             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
5961             var trans = {
5962                 id : transId,
5963                 cb : "stcCallback"+transId,
5964                 scriptId : "stcScript"+transId,
5965                 params : params,
5966                 arg : arg,
5967                 url : url,
5968                 callback : callback,
5969                 scope : scope,
5970                 reader : reader
5971             };
5972             var conn = this;
5973
5974             window[trans.cb] = function(o){
5975                 conn.handleResponse(o, trans);
5976             };
5977
5978             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
5979
5980             if(this.autoAbort !== false){
5981                 this.abort();
5982             }
5983
5984             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
5985
5986             var script = document.createElement("script");
5987             script.setAttribute("src", url);
5988             script.setAttribute("type", "text/javascript");
5989             script.setAttribute("id", trans.scriptId);
5990             this.head.appendChild(script);
5991
5992             this.trans = trans;
5993         }else{
5994             callback.call(scope||this, null, arg, false);
5995         }
5996     },
5997
5998     // private
5999     isLoading : function(){
6000         return this.trans ? true : false;
6001     },
6002
6003     /**
6004      * Abort the current server request.
6005      */
6006     abort : function(){
6007         if(this.isLoading()){
6008             this.destroyTrans(this.trans);
6009         }
6010     },
6011
6012     // private
6013     destroyTrans : function(trans, isLoaded){
6014         this.head.removeChild(document.getElementById(trans.scriptId));
6015         clearTimeout(trans.timeoutId);
6016         if(isLoaded){
6017             window[trans.cb] = undefined;
6018             try{
6019                 delete window[trans.cb];
6020             }catch(e){}
6021         }else{
6022             // if hasn't been loaded, wait for load to remove it to prevent script error
6023             window[trans.cb] = function(){
6024                 window[trans.cb] = undefined;
6025                 try{
6026                     delete window[trans.cb];
6027                 }catch(e){}
6028             };
6029         }
6030     },
6031
6032     // private
6033     handleResponse : function(o, trans){
6034         this.trans = false;
6035         this.destroyTrans(trans, true);
6036         var result;
6037         try {
6038             result = trans.reader.readRecords(o);
6039         }catch(e){
6040             this.fireEvent("loadexception", this, o, trans.arg, e);
6041             trans.callback.call(trans.scope||window, null, trans.arg, false);
6042             return;
6043         }
6044         this.fireEvent("load", this, o, trans.arg);
6045         trans.callback.call(trans.scope||window, result, trans.arg, true);
6046     },
6047
6048     // private
6049     handleFailure : function(trans){
6050         this.trans = false;
6051         this.destroyTrans(trans, false);
6052         this.fireEvent("loadexception", this, null, trans.arg);
6053         trans.callback.call(trans.scope||window, null, trans.arg, false);
6054     }
6055 });/*
6056  * Based on:
6057  * Ext JS Library 1.1.1
6058  * Copyright(c) 2006-2007, Ext JS, LLC.
6059  *
6060  * Originally Released Under LGPL - original licence link has changed is not relivant.
6061  *
6062  * Fork - LGPL
6063  * <script type="text/javascript">
6064  */
6065
6066 /**
6067  * @class Roo.data.JsonReader
6068  * @extends Roo.data.DataReader
6069  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6070  * based on mappings in a provided Roo.data.Record constructor.
6071  * 
6072  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6073  * in the reply previously. 
6074  * 
6075  * <p>
6076  * Example code:
6077  * <pre><code>
6078 var RecordDef = Roo.data.Record.create([
6079     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6080     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6081 ]);
6082 var myReader = new Roo.data.JsonReader({
6083     totalProperty: "results",    // The property which contains the total dataset size (optional)
6084     root: "rows",                // The property which contains an Array of row objects
6085     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6086 }, RecordDef);
6087 </code></pre>
6088  * <p>
6089  * This would consume a JSON file like this:
6090  * <pre><code>
6091 { 'results': 2, 'rows': [
6092     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6093     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6094 }
6095 </code></pre>
6096  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6097  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6098  * paged from the remote server.
6099  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6100  * @cfg {String} root name of the property which contains the Array of row objects.
6101  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6102  * @constructor
6103  * Create a new JsonReader
6104  * @param {Object} meta Metadata configuration options
6105  * @param {Object} recordType Either an Array of field definition objects,
6106  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6107  */
6108 Roo.data.JsonReader = function(meta, recordType){
6109     
6110     meta = meta || {};
6111     // set some defaults:
6112     Roo.applyIf(meta, {
6113         totalProperty: 'total',
6114         successProperty : 'success',
6115         root : 'data',
6116         id : 'id'
6117     });
6118     
6119     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6120 };
6121 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6122     
6123     /**
6124      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6125      * Used by Store query builder to append _requestMeta to params.
6126      * 
6127      */
6128     metaFromRemote : false,
6129     /**
6130      * This method is only used by a DataProxy which has retrieved data from a remote server.
6131      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6132      * @return {Object} data A data block which is used by an Roo.data.Store object as
6133      * a cache of Roo.data.Records.
6134      */
6135     read : function(response){
6136         var json = response.responseText;
6137        
6138         var o = /* eval:var:o */ eval("("+json+")");
6139         if(!o) {
6140             throw {message: "JsonReader.read: Json object not found"};
6141         }
6142         
6143         if(o.metaData){
6144             
6145             delete this.ef;
6146             this.metaFromRemote = true;
6147             this.meta = o.metaData;
6148             this.recordType = Roo.data.Record.create(o.metaData.fields);
6149             this.onMetaChange(this.meta, this.recordType, o);
6150         }
6151         return this.readRecords(o);
6152     },
6153
6154     // private function a store will implement
6155     onMetaChange : function(meta, recordType, o){
6156
6157     },
6158
6159     /**
6160          * @ignore
6161          */
6162     simpleAccess: function(obj, subsc) {
6163         return obj[subsc];
6164     },
6165
6166         /**
6167          * @ignore
6168          */
6169     getJsonAccessor: function(){
6170         var re = /[\[\.]/;
6171         return function(expr) {
6172             try {
6173                 return(re.test(expr))
6174                     ? new Function("obj", "return obj." + expr)
6175                     : function(obj){
6176                         return obj[expr];
6177                     };
6178             } catch(e){}
6179             return Roo.emptyFn;
6180         };
6181     }(),
6182
6183     /**
6184      * Create a data block containing Roo.data.Records from an XML document.
6185      * @param {Object} o An object which contains an Array of row objects in the property specified
6186      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6187      * which contains the total size of the dataset.
6188      * @return {Object} data A data block which is used by an Roo.data.Store object as
6189      * a cache of Roo.data.Records.
6190      */
6191     readRecords : function(o){
6192         /**
6193          * After any data loads, the raw JSON data is available for further custom processing.
6194          * @type Object
6195          */
6196         this.jsonData = o;
6197         var s = this.meta, Record = this.recordType,
6198             f = Record.prototype.fields, fi = f.items, fl = f.length;
6199
6200 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6201         if (!this.ef) {
6202             if(s.totalProperty) {
6203                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6204                 }
6205                 if(s.successProperty) {
6206                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6207                 }
6208                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6209                 if (s.id) {
6210                         var g = this.getJsonAccessor(s.id);
6211                         this.getId = function(rec) {
6212                                 var r = g(rec);
6213                                 return (r === undefined || r === "") ? null : r;
6214                         };
6215                 } else {
6216                         this.getId = function(){return null;};
6217                 }
6218             this.ef = [];
6219             for(var jj = 0; jj < fl; jj++){
6220                 f = fi[jj];
6221                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6222                 this.ef[jj] = this.getJsonAccessor(map);
6223             }
6224         }
6225
6226         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6227         if(s.totalProperty){
6228             var vt = parseInt(this.getTotal(o), 10);
6229             if(!isNaN(vt)){
6230                 totalRecords = vt;
6231             }
6232         }
6233         if(s.successProperty){
6234             var vs = this.getSuccess(o);
6235             if(vs === false || vs === 'false'){
6236                 success = false;
6237             }
6238         }
6239         var records = [];
6240             for(var i = 0; i < c; i++){
6241                     var n = root[i];
6242                 var values = {};
6243                 var id = this.getId(n);
6244                 for(var j = 0; j < fl; j++){
6245                     f = fi[j];
6246                 var v = this.ef[j](n);
6247                 if (!f.convert) {
6248                     Roo.log('missing convert for ' + f.name);
6249                     Roo.log(f);
6250                     continue;
6251                 }
6252                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6253                 }
6254                 var record = new Record(values, id);
6255                 record.json = n;
6256                 records[i] = record;
6257             }
6258             return {
6259                 success : success,
6260                 records : records,
6261                 totalRecords : totalRecords
6262             };
6263     }
6264 });/*
6265  * Based on:
6266  * Ext JS Library 1.1.1
6267  * Copyright(c) 2006-2007, Ext JS, LLC.
6268  *
6269  * Originally Released Under LGPL - original licence link has changed is not relivant.
6270  *
6271  * Fork - LGPL
6272  * <script type="text/javascript">
6273  */
6274
6275 /**
6276  * @class Roo.data.XmlReader
6277  * @extends Roo.data.DataReader
6278  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6279  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6280  * <p>
6281  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6282  * header in the HTTP response must be set to "text/xml".</em>
6283  * <p>
6284  * Example code:
6285  * <pre><code>
6286 var RecordDef = Roo.data.Record.create([
6287    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6288    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6289 ]);
6290 var myReader = new Roo.data.XmlReader({
6291    totalRecords: "results", // The element which contains the total dataset size (optional)
6292    record: "row",           // The repeated element which contains row information
6293    id: "id"                 // The element within the row that provides an ID for the record (optional)
6294 }, RecordDef);
6295 </code></pre>
6296  * <p>
6297  * This would consume an XML file like this:
6298  * <pre><code>
6299 &lt;?xml?>
6300 &lt;dataset>
6301  &lt;results>2&lt;/results>
6302  &lt;row>
6303    &lt;id>1&lt;/id>
6304    &lt;name>Bill&lt;/name>
6305    &lt;occupation>Gardener&lt;/occupation>
6306  &lt;/row>
6307  &lt;row>
6308    &lt;id>2&lt;/id>
6309    &lt;name>Ben&lt;/name>
6310    &lt;occupation>Horticulturalist&lt;/occupation>
6311  &lt;/row>
6312 &lt;/dataset>
6313 </code></pre>
6314  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6315  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6316  * paged from the remote server.
6317  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6318  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6319  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6320  * a record identifier value.
6321  * @constructor
6322  * Create a new XmlReader
6323  * @param {Object} meta Metadata configuration options
6324  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6325  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6326  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6327  */
6328 Roo.data.XmlReader = function(meta, recordType){
6329     meta = meta || {};
6330     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6331 };
6332 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6333     /**
6334      * This method is only used by a DataProxy which has retrieved data from a remote server.
6335          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6336          * to contain a method called 'responseXML' that returns an XML document object.
6337      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6338      * a cache of Roo.data.Records.
6339      */
6340     read : function(response){
6341         var doc = response.responseXML;
6342         if(!doc) {
6343             throw {message: "XmlReader.read: XML Document not available"};
6344         }
6345         return this.readRecords(doc);
6346     },
6347
6348     /**
6349      * Create a data block containing Roo.data.Records from an XML document.
6350          * @param {Object} doc A parsed XML document.
6351      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6352      * a cache of Roo.data.Records.
6353      */
6354     readRecords : function(doc){
6355         /**
6356          * After any data loads/reads, the raw XML Document is available for further custom processing.
6357          * @type XMLDocument
6358          */
6359         this.xmlData = doc;
6360         var root = doc.documentElement || doc;
6361         var q = Roo.DomQuery;
6362         var recordType = this.recordType, fields = recordType.prototype.fields;
6363         var sid = this.meta.id;
6364         var totalRecords = 0, success = true;
6365         if(this.meta.totalRecords){
6366             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6367         }
6368         
6369         if(this.meta.success){
6370             var sv = q.selectValue(this.meta.success, root, true);
6371             success = sv !== false && sv !== 'false';
6372         }
6373         var records = [];
6374         var ns = q.select(this.meta.record, root);
6375         for(var i = 0, len = ns.length; i < len; i++) {
6376                 var n = ns[i];
6377                 var values = {};
6378                 var id = sid ? q.selectValue(sid, n) : undefined;
6379                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6380                     var f = fields.items[j];
6381                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6382                     v = f.convert(v);
6383                     values[f.name] = v;
6384                 }
6385                 var record = new recordType(values, id);
6386                 record.node = n;
6387                 records[records.length] = record;
6388             }
6389
6390             return {
6391                 success : success,
6392                 records : records,
6393                 totalRecords : totalRecords || records.length
6394             };
6395     }
6396 });/*
6397  * Based on:
6398  * Ext JS Library 1.1.1
6399  * Copyright(c) 2006-2007, Ext JS, LLC.
6400  *
6401  * Originally Released Under LGPL - original licence link has changed is not relivant.
6402  *
6403  * Fork - LGPL
6404  * <script type="text/javascript">
6405  */
6406
6407 /**
6408  * @class Roo.data.ArrayReader
6409  * @extends Roo.data.DataReader
6410  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6411  * Each element of that Array represents a row of data fields. The
6412  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6413  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6414  * <p>
6415  * Example code:.
6416  * <pre><code>
6417 var RecordDef = Roo.data.Record.create([
6418     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6419     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6420 ]);
6421 var myReader = new Roo.data.ArrayReader({
6422     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6423 }, RecordDef);
6424 </code></pre>
6425  * <p>
6426  * This would consume an Array like this:
6427  * <pre><code>
6428 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6429   </code></pre>
6430  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6431  * @constructor
6432  * Create a new JsonReader
6433  * @param {Object} meta Metadata configuration options.
6434  * @param {Object} recordType Either an Array of field definition objects
6435  * as specified to {@link Roo.data.Record#create},
6436  * or an {@link Roo.data.Record} object
6437  * created using {@link Roo.data.Record#create}.
6438  */
6439 Roo.data.ArrayReader = function(meta, recordType){
6440     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6441 };
6442
6443 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6444     /**
6445      * Create a data block containing Roo.data.Records from an XML document.
6446      * @param {Object} o An Array of row objects which represents the dataset.
6447      * @return {Object} data A data block which is used by an Roo.data.Store object as
6448      * a cache of Roo.data.Records.
6449      */
6450     readRecords : function(o){
6451         var sid = this.meta ? this.meta.id : null;
6452         var recordType = this.recordType, fields = recordType.prototype.fields;
6453         var records = [];
6454         var root = o;
6455             for(var i = 0; i < root.length; i++){
6456                     var n = root[i];
6457                 var values = {};
6458                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6459                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6460                 var f = fields.items[j];
6461                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6462                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6463                 v = f.convert(v);
6464                 values[f.name] = v;
6465             }
6466                 var record = new recordType(values, id);
6467                 record.json = n;
6468                 records[records.length] = record;
6469             }
6470             return {
6471                 records : records,
6472                 totalRecords : records.length
6473             };
6474     }
6475 });/*
6476  * Based on:
6477  * Ext JS Library 1.1.1
6478  * Copyright(c) 2006-2007, Ext JS, LLC.
6479  *
6480  * Originally Released Under LGPL - original licence link has changed is not relivant.
6481  *
6482  * Fork - LGPL
6483  * <script type="text/javascript">
6484  */
6485
6486
6487 /**
6488  * @class Roo.data.Tree
6489  * @extends Roo.util.Observable
6490  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6491  * in the tree have most standard DOM functionality.
6492  * @constructor
6493  * @param {Node} root (optional) The root node
6494  */
6495 Roo.data.Tree = function(root){
6496    this.nodeHash = {};
6497    /**
6498     * The root node for this tree
6499     * @type Node
6500     */
6501    this.root = null;
6502    if(root){
6503        this.setRootNode(root);
6504    }
6505    this.addEvents({
6506        /**
6507         * @event append
6508         * Fires when a new child node is appended to a node in this tree.
6509         * @param {Tree} tree The owner tree
6510         * @param {Node} parent The parent node
6511         * @param {Node} node The newly appended node
6512         * @param {Number} index The index of the newly appended node
6513         */
6514        "append" : true,
6515        /**
6516         * @event remove
6517         * Fires when a child node is removed from a node in this tree.
6518         * @param {Tree} tree The owner tree
6519         * @param {Node} parent The parent node
6520         * @param {Node} node The child node removed
6521         */
6522        "remove" : true,
6523        /**
6524         * @event move
6525         * Fires when a node is moved to a new location in the tree
6526         * @param {Tree} tree The owner tree
6527         * @param {Node} node The node moved
6528         * @param {Node} oldParent The old parent of this node
6529         * @param {Node} newParent The new parent of this node
6530         * @param {Number} index The index it was moved to
6531         */
6532        "move" : true,
6533        /**
6534         * @event insert
6535         * Fires when a new child node is inserted in a node in this tree.
6536         * @param {Tree} tree The owner tree
6537         * @param {Node} parent The parent node
6538         * @param {Node} node The child node inserted
6539         * @param {Node} refNode The child node the node was inserted before
6540         */
6541        "insert" : true,
6542        /**
6543         * @event beforeappend
6544         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6545         * @param {Tree} tree The owner tree
6546         * @param {Node} parent The parent node
6547         * @param {Node} node The child node to be appended
6548         */
6549        "beforeappend" : true,
6550        /**
6551         * @event beforeremove
6552         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6553         * @param {Tree} tree The owner tree
6554         * @param {Node} parent The parent node
6555         * @param {Node} node The child node to be removed
6556         */
6557        "beforeremove" : true,
6558        /**
6559         * @event beforemove
6560         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6561         * @param {Tree} tree The owner tree
6562         * @param {Node} node The node being moved
6563         * @param {Node} oldParent The parent of the node
6564         * @param {Node} newParent The new parent the node is moving to
6565         * @param {Number} index The index it is being moved to
6566         */
6567        "beforemove" : true,
6568        /**
6569         * @event beforeinsert
6570         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6571         * @param {Tree} tree The owner tree
6572         * @param {Node} parent The parent node
6573         * @param {Node} node The child node to be inserted
6574         * @param {Node} refNode The child node the node is being inserted before
6575         */
6576        "beforeinsert" : true
6577    });
6578
6579     Roo.data.Tree.superclass.constructor.call(this);
6580 };
6581
6582 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6583     pathSeparator: "/",
6584
6585     proxyNodeEvent : function(){
6586         return this.fireEvent.apply(this, arguments);
6587     },
6588
6589     /**
6590      * Returns the root node for this tree.
6591      * @return {Node}
6592      */
6593     getRootNode : function(){
6594         return this.root;
6595     },
6596
6597     /**
6598      * Sets the root node for this tree.
6599      * @param {Node} node
6600      * @return {Node}
6601      */
6602     setRootNode : function(node){
6603         this.root = node;
6604         node.ownerTree = this;
6605         node.isRoot = true;
6606         this.registerNode(node);
6607         return node;
6608     },
6609
6610     /**
6611      * Gets a node in this tree by its id.
6612      * @param {String} id
6613      * @return {Node}
6614      */
6615     getNodeById : function(id){
6616         return this.nodeHash[id];
6617     },
6618
6619     registerNode : function(node){
6620         this.nodeHash[node.id] = node;
6621     },
6622
6623     unregisterNode : function(node){
6624         delete this.nodeHash[node.id];
6625     },
6626
6627     toString : function(){
6628         return "[Tree"+(this.id?" "+this.id:"")+"]";
6629     }
6630 });
6631
6632 /**
6633  * @class Roo.data.Node
6634  * @extends Roo.util.Observable
6635  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6636  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6637  * @constructor
6638  * @param {Object} attributes The attributes/config for the node
6639  */
6640 Roo.data.Node = function(attributes){
6641     /**
6642      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6643      * @type {Object}
6644      */
6645     this.attributes = attributes || {};
6646     this.leaf = this.attributes.leaf;
6647     /**
6648      * The node id. @type String
6649      */
6650     this.id = this.attributes.id;
6651     if(!this.id){
6652         this.id = Roo.id(null, "ynode-");
6653         this.attributes.id = this.id;
6654     }
6655     /**
6656      * All child nodes of this node. @type Array
6657      */
6658     this.childNodes = [];
6659     if(!this.childNodes.indexOf){ // indexOf is a must
6660         this.childNodes.indexOf = function(o){
6661             for(var i = 0, len = this.length; i < len; i++){
6662                 if(this[i] == o) {
6663                     return i;
6664                 }
6665             }
6666             return -1;
6667         };
6668     }
6669     /**
6670      * The parent node for this node. @type Node
6671      */
6672     this.parentNode = null;
6673     /**
6674      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6675      */
6676     this.firstChild = null;
6677     /**
6678      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6679      */
6680     this.lastChild = null;
6681     /**
6682      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6683      */
6684     this.previousSibling = null;
6685     /**
6686      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6687      */
6688     this.nextSibling = null;
6689
6690     this.addEvents({
6691        /**
6692         * @event append
6693         * Fires when a new child node is appended
6694         * @param {Tree} tree The owner tree
6695         * @param {Node} this This node
6696         * @param {Node} node The newly appended node
6697         * @param {Number} index The index of the newly appended node
6698         */
6699        "append" : true,
6700        /**
6701         * @event remove
6702         * Fires when a child node is removed
6703         * @param {Tree} tree The owner tree
6704         * @param {Node} this This node
6705         * @param {Node} node The removed node
6706         */
6707        "remove" : true,
6708        /**
6709         * @event move
6710         * Fires when this node is moved to a new location in the tree
6711         * @param {Tree} tree The owner tree
6712         * @param {Node} this This node
6713         * @param {Node} oldParent The old parent of this node
6714         * @param {Node} newParent The new parent of this node
6715         * @param {Number} index The index it was moved to
6716         */
6717        "move" : true,
6718        /**
6719         * @event insert
6720         * Fires when a new child node is inserted.
6721         * @param {Tree} tree The owner tree
6722         * @param {Node} this This node
6723         * @param {Node} node The child node inserted
6724         * @param {Node} refNode The child node the node was inserted before
6725         */
6726        "insert" : true,
6727        /**
6728         * @event beforeappend
6729         * Fires before a new child is appended, return false to cancel the append.
6730         * @param {Tree} tree The owner tree
6731         * @param {Node} this This node
6732         * @param {Node} node The child node to be appended
6733         */
6734        "beforeappend" : true,
6735        /**
6736         * @event beforeremove
6737         * Fires before a child is removed, return false to cancel the remove.
6738         * @param {Tree} tree The owner tree
6739         * @param {Node} this This node
6740         * @param {Node} node The child node to be removed
6741         */
6742        "beforeremove" : true,
6743        /**
6744         * @event beforemove
6745         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6746         * @param {Tree} tree The owner tree
6747         * @param {Node} this This node
6748         * @param {Node} oldParent The parent of this node
6749         * @param {Node} newParent The new parent this node is moving to
6750         * @param {Number} index The index it is being moved to
6751         */
6752        "beforemove" : true,
6753        /**
6754         * @event beforeinsert
6755         * Fires before a new child is inserted, return false to cancel the insert.
6756         * @param {Tree} tree The owner tree
6757         * @param {Node} this This node
6758         * @param {Node} node The child node to be inserted
6759         * @param {Node} refNode The child node the node is being inserted before
6760         */
6761        "beforeinsert" : true
6762    });
6763     this.listeners = this.attributes.listeners;
6764     Roo.data.Node.superclass.constructor.call(this);
6765 };
6766
6767 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6768     fireEvent : function(evtName){
6769         // first do standard event for this node
6770         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6771             return false;
6772         }
6773         // then bubble it up to the tree if the event wasn't cancelled
6774         var ot = this.getOwnerTree();
6775         if(ot){
6776             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6777                 return false;
6778             }
6779         }
6780         return true;
6781     },
6782
6783     /**
6784      * Returns true if this node is a leaf
6785      * @return {Boolean}
6786      */
6787     isLeaf : function(){
6788         return this.leaf === true;
6789     },
6790
6791     // private
6792     setFirstChild : function(node){
6793         this.firstChild = node;
6794     },
6795
6796     //private
6797     setLastChild : function(node){
6798         this.lastChild = node;
6799     },
6800
6801
6802     /**
6803      * Returns true if this node is the last child of its parent
6804      * @return {Boolean}
6805      */
6806     isLast : function(){
6807        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6808     },
6809
6810     /**
6811      * Returns true if this node is the first child of its parent
6812      * @return {Boolean}
6813      */
6814     isFirst : function(){
6815        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6816     },
6817
6818     hasChildNodes : function(){
6819         return !this.isLeaf() && this.childNodes.length > 0;
6820     },
6821
6822     /**
6823      * Insert node(s) as the last child node of this node.
6824      * @param {Node/Array} node The node or Array of nodes to append
6825      * @return {Node} The appended node if single append, or null if an array was passed
6826      */
6827     appendChild : function(node){
6828         var multi = false;
6829         if(node instanceof Array){
6830             multi = node;
6831         }else if(arguments.length > 1){
6832             multi = arguments;
6833         }
6834         // if passed an array or multiple args do them one by one
6835         if(multi){
6836             for(var i = 0, len = multi.length; i < len; i++) {
6837                 this.appendChild(multi[i]);
6838             }
6839         }else{
6840             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6841                 return false;
6842             }
6843             var index = this.childNodes.length;
6844             var oldParent = node.parentNode;
6845             // it's a move, make sure we move it cleanly
6846             if(oldParent){
6847                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6848                     return false;
6849                 }
6850                 oldParent.removeChild(node);
6851             }
6852             index = this.childNodes.length;
6853             if(index == 0){
6854                 this.setFirstChild(node);
6855             }
6856             this.childNodes.push(node);
6857             node.parentNode = this;
6858             var ps = this.childNodes[index-1];
6859             if(ps){
6860                 node.previousSibling = ps;
6861                 ps.nextSibling = node;
6862             }else{
6863                 node.previousSibling = null;
6864             }
6865             node.nextSibling = null;
6866             this.setLastChild(node);
6867             node.setOwnerTree(this.getOwnerTree());
6868             this.fireEvent("append", this.ownerTree, this, node, index);
6869             if(oldParent){
6870                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6871             }
6872             return node;
6873         }
6874     },
6875
6876     /**
6877      * Removes a child node from this node.
6878      * @param {Node} node The node to remove
6879      * @return {Node} The removed node
6880      */
6881     removeChild : function(node){
6882         var index = this.childNodes.indexOf(node);
6883         if(index == -1){
6884             return false;
6885         }
6886         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6887             return false;
6888         }
6889
6890         // remove it from childNodes collection
6891         this.childNodes.splice(index, 1);
6892
6893         // update siblings
6894         if(node.previousSibling){
6895             node.previousSibling.nextSibling = node.nextSibling;
6896         }
6897         if(node.nextSibling){
6898             node.nextSibling.previousSibling = node.previousSibling;
6899         }
6900
6901         // update child refs
6902         if(this.firstChild == node){
6903             this.setFirstChild(node.nextSibling);
6904         }
6905         if(this.lastChild == node){
6906             this.setLastChild(node.previousSibling);
6907         }
6908
6909         node.setOwnerTree(null);
6910         // clear any references from the node
6911         node.parentNode = null;
6912         node.previousSibling = null;
6913         node.nextSibling = null;
6914         this.fireEvent("remove", this.ownerTree, this, node);
6915         return node;
6916     },
6917
6918     /**
6919      * Inserts the first node before the second node in this nodes childNodes collection.
6920      * @param {Node} node The node to insert
6921      * @param {Node} refNode The node to insert before (if null the node is appended)
6922      * @return {Node} The inserted node
6923      */
6924     insertBefore : function(node, refNode){
6925         if(!refNode){ // like standard Dom, refNode can be null for append
6926             return this.appendChild(node);
6927         }
6928         // nothing to do
6929         if(node == refNode){
6930             return false;
6931         }
6932
6933         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6934             return false;
6935         }
6936         var index = this.childNodes.indexOf(refNode);
6937         var oldParent = node.parentNode;
6938         var refIndex = index;
6939
6940         // when moving internally, indexes will change after remove
6941         if(oldParent == this && this.childNodes.indexOf(node) < index){
6942             refIndex--;
6943         }
6944
6945         // it's a move, make sure we move it cleanly
6946         if(oldParent){
6947             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
6948                 return false;
6949             }
6950             oldParent.removeChild(node);
6951         }
6952         if(refIndex == 0){
6953             this.setFirstChild(node);
6954         }
6955         this.childNodes.splice(refIndex, 0, node);
6956         node.parentNode = this;
6957         var ps = this.childNodes[refIndex-1];
6958         if(ps){
6959             node.previousSibling = ps;
6960             ps.nextSibling = node;
6961         }else{
6962             node.previousSibling = null;
6963         }
6964         node.nextSibling = refNode;
6965         refNode.previousSibling = node;
6966         node.setOwnerTree(this.getOwnerTree());
6967         this.fireEvent("insert", this.ownerTree, this, node, refNode);
6968         if(oldParent){
6969             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
6970         }
6971         return node;
6972     },
6973
6974     /**
6975      * Returns the child node at the specified index.
6976      * @param {Number} index
6977      * @return {Node}
6978      */
6979     item : function(index){
6980         return this.childNodes[index];
6981     },
6982
6983     /**
6984      * Replaces one child node in this node with another.
6985      * @param {Node} newChild The replacement node
6986      * @param {Node} oldChild The node to replace
6987      * @return {Node} The replaced node
6988      */
6989     replaceChild : function(newChild, oldChild){
6990         this.insertBefore(newChild, oldChild);
6991         this.removeChild(oldChild);
6992         return oldChild;
6993     },
6994
6995     /**
6996      * Returns the index of a child node
6997      * @param {Node} node
6998      * @return {Number} The index of the node or -1 if it was not found
6999      */
7000     indexOf : function(child){
7001         return this.childNodes.indexOf(child);
7002     },
7003
7004     /**
7005      * Returns the tree this node is in.
7006      * @return {Tree}
7007      */
7008     getOwnerTree : function(){
7009         // if it doesn't have one, look for one
7010         if(!this.ownerTree){
7011             var p = this;
7012             while(p){
7013                 if(p.ownerTree){
7014                     this.ownerTree = p.ownerTree;
7015                     break;
7016                 }
7017                 p = p.parentNode;
7018             }
7019         }
7020         return this.ownerTree;
7021     },
7022
7023     /**
7024      * Returns depth of this node (the root node has a depth of 0)
7025      * @return {Number}
7026      */
7027     getDepth : function(){
7028         var depth = 0;
7029         var p = this;
7030         while(p.parentNode){
7031             ++depth;
7032             p = p.parentNode;
7033         }
7034         return depth;
7035     },
7036
7037     // private
7038     setOwnerTree : function(tree){
7039         // if it's move, we need to update everyone
7040         if(tree != this.ownerTree){
7041             if(this.ownerTree){
7042                 this.ownerTree.unregisterNode(this);
7043             }
7044             this.ownerTree = tree;
7045             var cs = this.childNodes;
7046             for(var i = 0, len = cs.length; i < len; i++) {
7047                 cs[i].setOwnerTree(tree);
7048             }
7049             if(tree){
7050                 tree.registerNode(this);
7051             }
7052         }
7053     },
7054
7055     /**
7056      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7057      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7058      * @return {String} The path
7059      */
7060     getPath : function(attr){
7061         attr = attr || "id";
7062         var p = this.parentNode;
7063         var b = [this.attributes[attr]];
7064         while(p){
7065             b.unshift(p.attributes[attr]);
7066             p = p.parentNode;
7067         }
7068         var sep = this.getOwnerTree().pathSeparator;
7069         return sep + b.join(sep);
7070     },
7071
7072     /**
7073      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7074      * function call will be the scope provided or the current node. The arguments to the function
7075      * will be the args provided or the current node. If the function returns false at any point,
7076      * the bubble is stopped.
7077      * @param {Function} fn The function to call
7078      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7079      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7080      */
7081     bubble : function(fn, scope, args){
7082         var p = this;
7083         while(p){
7084             if(fn.call(scope || p, args || p) === false){
7085                 break;
7086             }
7087             p = p.parentNode;
7088         }
7089     },
7090
7091     /**
7092      * Cascades down 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 cascade is stopped on that branch.
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     cascade : function(fn, scope, args){
7101         if(fn.call(scope || this, args || this) !== false){
7102             var cs = this.childNodes;
7103             for(var i = 0, len = cs.length; i < len; i++) {
7104                 cs[i].cascade(fn, scope, args);
7105             }
7106         }
7107     },
7108
7109     /**
7110      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7111      * function call will be the scope provided or the current node. The arguments to the function
7112      * will be the args provided or the current node. If the function returns false at any point,
7113      * the iteration stops.
7114      * @param {Function} fn The function to call
7115      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7116      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7117      */
7118     eachChild : function(fn, scope, args){
7119         var cs = this.childNodes;
7120         for(var i = 0, len = cs.length; i < len; i++) {
7121                 if(fn.call(scope || this, args || cs[i]) === false){
7122                     break;
7123                 }
7124         }
7125     },
7126
7127     /**
7128      * Finds the first child that has the attribute with the specified value.
7129      * @param {String} attribute The attribute name
7130      * @param {Mixed} value The value to search for
7131      * @return {Node} The found child or null if none was found
7132      */
7133     findChild : function(attribute, value){
7134         var cs = this.childNodes;
7135         for(var i = 0, len = cs.length; i < len; i++) {
7136                 if(cs[i].attributes[attribute] == value){
7137                     return cs[i];
7138                 }
7139         }
7140         return null;
7141     },
7142
7143     /**
7144      * Finds the first child by a custom function. The child matches if the function passed
7145      * returns true.
7146      * @param {Function} fn
7147      * @param {Object} scope (optional)
7148      * @return {Node} The found child or null if none was found
7149      */
7150     findChildBy : function(fn, scope){
7151         var cs = this.childNodes;
7152         for(var i = 0, len = cs.length; i < len; i++) {
7153                 if(fn.call(scope||cs[i], cs[i]) === true){
7154                     return cs[i];
7155                 }
7156         }
7157         return null;
7158     },
7159
7160     /**
7161      * Sorts this nodes children using the supplied sort function
7162      * @param {Function} fn
7163      * @param {Object} scope (optional)
7164      */
7165     sort : function(fn, scope){
7166         var cs = this.childNodes;
7167         var len = cs.length;
7168         if(len > 0){
7169             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7170             cs.sort(sortFn);
7171             for(var i = 0; i < len; i++){
7172                 var n = cs[i];
7173                 n.previousSibling = cs[i-1];
7174                 n.nextSibling = cs[i+1];
7175                 if(i == 0){
7176                     this.setFirstChild(n);
7177                 }
7178                 if(i == len-1){
7179                     this.setLastChild(n);
7180                 }
7181             }
7182         }
7183     },
7184
7185     /**
7186      * Returns true if this node is an ancestor (at any point) of the passed node.
7187      * @param {Node} node
7188      * @return {Boolean}
7189      */
7190     contains : function(node){
7191         return node.isAncestor(this);
7192     },
7193
7194     /**
7195      * Returns true if the passed node is an ancestor (at any point) of this node.
7196      * @param {Node} node
7197      * @return {Boolean}
7198      */
7199     isAncestor : function(node){
7200         var p = this.parentNode;
7201         while(p){
7202             if(p == node){
7203                 return true;
7204             }
7205             p = p.parentNode;
7206         }
7207         return false;
7208     },
7209
7210     toString : function(){
7211         return "[Node"+(this.id?" "+this.id:"")+"]";
7212     }
7213 });/*
7214  * Based on:
7215  * Ext JS Library 1.1.1
7216  * Copyright(c) 2006-2007, Ext JS, LLC.
7217  *
7218  * Originally Released Under LGPL - original licence link has changed is not relivant.
7219  *
7220  * Fork - LGPL
7221  * <script type="text/javascript">
7222  */
7223  
7224
7225 /**
7226  * @class Roo.ComponentMgr
7227  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7228  * @singleton
7229  */
7230 Roo.ComponentMgr = function(){
7231     var all = new Roo.util.MixedCollection();
7232
7233     return {
7234         /**
7235          * Registers a component.
7236          * @param {Roo.Component} c The component
7237          */
7238         register : function(c){
7239             all.add(c);
7240         },
7241
7242         /**
7243          * Unregisters a component.
7244          * @param {Roo.Component} c The component
7245          */
7246         unregister : function(c){
7247             all.remove(c);
7248         },
7249
7250         /**
7251          * Returns a component by id
7252          * @param {String} id The component id
7253          */
7254         get : function(id){
7255             return all.get(id);
7256         },
7257
7258         /**
7259          * Registers a function that will be called when a specified component is added to ComponentMgr
7260          * @param {String} id The component id
7261          * @param {Funtction} fn The callback function
7262          * @param {Object} scope The scope of the callback
7263          */
7264         onAvailable : function(id, fn, scope){
7265             all.on("add", function(index, o){
7266                 if(o.id == id){
7267                     fn.call(scope || o, o);
7268                     all.un("add", fn, scope);
7269                 }
7270             });
7271         }
7272     };
7273 }();/*
7274  * Based on:
7275  * Ext JS Library 1.1.1
7276  * Copyright(c) 2006-2007, Ext JS, LLC.
7277  *
7278  * Originally Released Under LGPL - original licence link has changed is not relivant.
7279  *
7280  * Fork - LGPL
7281  * <script type="text/javascript">
7282  */
7283  
7284 /**
7285  * @class Roo.Component
7286  * @extends Roo.util.Observable
7287  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7288  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7289  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7290  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7291  * All visual components (widgets) that require rendering into a layout should subclass Component.
7292  * @constructor
7293  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7294  * 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
7295  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7296  */
7297 Roo.Component = function(config){
7298     config = config || {};
7299     if(config.tagName || config.dom || typeof config == "string"){ // element object
7300         config = {el: config, id: config.id || config};
7301     }
7302     this.initialConfig = config;
7303
7304     Roo.apply(this, config);
7305     this.addEvents({
7306         /**
7307          * @event disable
7308          * Fires after the component is disabled.
7309              * @param {Roo.Component} this
7310              */
7311         disable : true,
7312         /**
7313          * @event enable
7314          * Fires after the component is enabled.
7315              * @param {Roo.Component} this
7316              */
7317         enable : true,
7318         /**
7319          * @event beforeshow
7320          * Fires before the component is shown.  Return false to stop the show.
7321              * @param {Roo.Component} this
7322              */
7323         beforeshow : true,
7324         /**
7325          * @event show
7326          * Fires after the component is shown.
7327              * @param {Roo.Component} this
7328              */
7329         show : true,
7330         /**
7331          * @event beforehide
7332          * Fires before the component is hidden. Return false to stop the hide.
7333              * @param {Roo.Component} this
7334              */
7335         beforehide : true,
7336         /**
7337          * @event hide
7338          * Fires after the component is hidden.
7339              * @param {Roo.Component} this
7340              */
7341         hide : true,
7342         /**
7343          * @event beforerender
7344          * Fires before the component is rendered. Return false to stop the render.
7345              * @param {Roo.Component} this
7346              */
7347         beforerender : true,
7348         /**
7349          * @event render
7350          * Fires after the component is rendered.
7351              * @param {Roo.Component} this
7352              */
7353         render : true,
7354         /**
7355          * @event beforedestroy
7356          * Fires before the component is destroyed. Return false to stop the destroy.
7357              * @param {Roo.Component} this
7358              */
7359         beforedestroy : true,
7360         /**
7361          * @event destroy
7362          * Fires after the component is destroyed.
7363              * @param {Roo.Component} this
7364              */
7365         destroy : true
7366     });
7367     if(!this.id){
7368         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7369     }
7370     Roo.ComponentMgr.register(this);
7371     Roo.Component.superclass.constructor.call(this);
7372     this.initComponent();
7373     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7374         this.render(this.renderTo);
7375         delete this.renderTo;
7376     }
7377 };
7378
7379 // private
7380 Roo.Component.AUTO_ID = 1000;
7381
7382 Roo.extend(Roo.Component, Roo.util.Observable, {
7383     /**
7384      * @property {Boolean} hidden
7385      * true if this component is hidden. Read-only.
7386      */
7387     hidden : false,
7388     /**
7389      * true if this component is disabled. Read-only.
7390      */
7391     disabled : false,
7392     /**
7393      * true if this component has been rendered. Read-only.
7394      */
7395     rendered : false,
7396     
7397     /** @cfg {String} disableClass
7398      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7399      */
7400     disabledClass : "x-item-disabled",
7401         /** @cfg {Boolean} allowDomMove
7402          * Whether the component can move the Dom node when rendering (defaults to true).
7403          */
7404     allowDomMove : true,
7405     /** @cfg {String} hideMode
7406      * How this component should hidden. Supported values are
7407      * "visibility" (css visibility), "offsets" (negative offset position) and
7408      * "display" (css display) - defaults to "display".
7409      */
7410     hideMode: 'display',
7411
7412     // private
7413     ctype : "Roo.Component",
7414
7415     /** @cfg {String} actionMode 
7416      * which property holds the element that used for  hide() / show() / disable() / enable()
7417      * default is 'el' 
7418      */
7419     actionMode : "el",
7420
7421     // private
7422     getActionEl : function(){
7423         return this[this.actionMode];
7424     },
7425
7426     initComponent : Roo.emptyFn,
7427     /**
7428      * If this is a lazy rendering component, render it to its container element.
7429      * @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.
7430      */
7431     render : function(container, position){
7432         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7433             if(!container && this.el){
7434                 this.el = Roo.get(this.el);
7435                 container = this.el.dom.parentNode;
7436                 this.allowDomMove = false;
7437             }
7438             this.container = Roo.get(container);
7439             this.rendered = true;
7440             if(position !== undefined){
7441                 if(typeof position == 'number'){
7442                     position = this.container.dom.childNodes[position];
7443                 }else{
7444                     position = Roo.getDom(position);
7445                 }
7446             }
7447             this.onRender(this.container, position || null);
7448             if(this.cls){
7449                 this.el.addClass(this.cls);
7450                 delete this.cls;
7451             }
7452             if(this.style){
7453                 this.el.applyStyles(this.style);
7454                 delete this.style;
7455             }
7456             this.fireEvent("render", this);
7457             this.afterRender(this.container);
7458             if(this.hidden){
7459                 this.hide();
7460             }
7461             if(this.disabled){
7462                 this.disable();
7463             }
7464         }
7465         return this;
7466     },
7467
7468     // private
7469     // default function is not really useful
7470     onRender : function(ct, position){
7471         if(this.el){
7472             this.el = Roo.get(this.el);
7473             if(this.allowDomMove !== false){
7474                 ct.dom.insertBefore(this.el.dom, position);
7475             }
7476         }
7477     },
7478
7479     // private
7480     getAutoCreate : function(){
7481         var cfg = typeof this.autoCreate == "object" ?
7482                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7483         if(this.id && !cfg.id){
7484             cfg.id = this.id;
7485         }
7486         return cfg;
7487     },
7488
7489     // private
7490     afterRender : Roo.emptyFn,
7491
7492     /**
7493      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7494      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7495      */
7496     destroy : function(){
7497         if(this.fireEvent("beforedestroy", this) !== false){
7498             this.purgeListeners();
7499             this.beforeDestroy();
7500             if(this.rendered){
7501                 this.el.removeAllListeners();
7502                 this.el.remove();
7503                 if(this.actionMode == "container"){
7504                     this.container.remove();
7505                 }
7506             }
7507             this.onDestroy();
7508             Roo.ComponentMgr.unregister(this);
7509             this.fireEvent("destroy", this);
7510         }
7511     },
7512
7513         // private
7514     beforeDestroy : function(){
7515
7516     },
7517
7518         // private
7519         onDestroy : function(){
7520
7521     },
7522
7523     /**
7524      * Returns the underlying {@link Roo.Element}.
7525      * @return {Roo.Element} The element
7526      */
7527     getEl : function(){
7528         return this.el;
7529     },
7530
7531     /**
7532      * Returns the id of this component.
7533      * @return {String}
7534      */
7535     getId : function(){
7536         return this.id;
7537     },
7538
7539     /**
7540      * Try to focus this component.
7541      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7542      * @return {Roo.Component} this
7543      */
7544     focus : function(selectText){
7545         if(this.rendered){
7546             this.el.focus();
7547             if(selectText === true){
7548                 this.el.dom.select();
7549             }
7550         }
7551         return this;
7552     },
7553
7554     // private
7555     blur : function(){
7556         if(this.rendered){
7557             this.el.blur();
7558         }
7559         return this;
7560     },
7561
7562     /**
7563      * Disable this component.
7564      * @return {Roo.Component} this
7565      */
7566     disable : function(){
7567         if(this.rendered){
7568             this.onDisable();
7569         }
7570         this.disabled = true;
7571         this.fireEvent("disable", this);
7572         return this;
7573     },
7574
7575         // private
7576     onDisable : function(){
7577         this.getActionEl().addClass(this.disabledClass);
7578         this.el.dom.disabled = true;
7579     },
7580
7581     /**
7582      * Enable this component.
7583      * @return {Roo.Component} this
7584      */
7585     enable : function(){
7586         if(this.rendered){
7587             this.onEnable();
7588         }
7589         this.disabled = false;
7590         this.fireEvent("enable", this);
7591         return this;
7592     },
7593
7594         // private
7595     onEnable : function(){
7596         this.getActionEl().removeClass(this.disabledClass);
7597         this.el.dom.disabled = false;
7598     },
7599
7600     /**
7601      * Convenience function for setting disabled/enabled by boolean.
7602      * @param {Boolean} disabled
7603      */
7604     setDisabled : function(disabled){
7605         this[disabled ? "disable" : "enable"]();
7606     },
7607
7608     /**
7609      * Show this component.
7610      * @return {Roo.Component} this
7611      */
7612     show: function(){
7613         if(this.fireEvent("beforeshow", this) !== false){
7614             this.hidden = false;
7615             if(this.rendered){
7616                 this.onShow();
7617             }
7618             this.fireEvent("show", this);
7619         }
7620         return this;
7621     },
7622
7623     // private
7624     onShow : function(){
7625         var ae = this.getActionEl();
7626         if(this.hideMode == 'visibility'){
7627             ae.dom.style.visibility = "visible";
7628         }else if(this.hideMode == 'offsets'){
7629             ae.removeClass('x-hidden');
7630         }else{
7631             ae.dom.style.display = "";
7632         }
7633     },
7634
7635     /**
7636      * Hide this component.
7637      * @return {Roo.Component} this
7638      */
7639     hide: function(){
7640         if(this.fireEvent("beforehide", this) !== false){
7641             this.hidden = true;
7642             if(this.rendered){
7643                 this.onHide();
7644             }
7645             this.fireEvent("hide", this);
7646         }
7647         return this;
7648     },
7649
7650     // private
7651     onHide : function(){
7652         var ae = this.getActionEl();
7653         if(this.hideMode == 'visibility'){
7654             ae.dom.style.visibility = "hidden";
7655         }else if(this.hideMode == 'offsets'){
7656             ae.addClass('x-hidden');
7657         }else{
7658             ae.dom.style.display = "none";
7659         }
7660     },
7661
7662     /**
7663      * Convenience function to hide or show this component by boolean.
7664      * @param {Boolean} visible True to show, false to hide
7665      * @return {Roo.Component} this
7666      */
7667     setVisible: function(visible){
7668         if(visible) {
7669             this.show();
7670         }else{
7671             this.hide();
7672         }
7673         return this;
7674     },
7675
7676     /**
7677      * Returns true if this component is visible.
7678      */
7679     isVisible : function(){
7680         return this.getActionEl().isVisible();
7681     },
7682
7683     cloneConfig : function(overrides){
7684         overrides = overrides || {};
7685         var id = overrides.id || Roo.id();
7686         var cfg = Roo.applyIf(overrides, this.initialConfig);
7687         cfg.id = id; // prevent dup id
7688         return new this.constructor(cfg);
7689     }
7690 });/*
7691  * Based on:
7692  * Ext JS Library 1.1.1
7693  * Copyright(c) 2006-2007, Ext JS, LLC.
7694  *
7695  * Originally Released Under LGPL - original licence link has changed is not relivant.
7696  *
7697  * Fork - LGPL
7698  * <script type="text/javascript">
7699  */
7700  (function(){ 
7701 /**
7702  * @class Roo.Layer
7703  * @extends Roo.Element
7704  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7705  * automatic maintaining of shadow/shim positions.
7706  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7707  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7708  * you can pass a string with a CSS class name. False turns off the shadow.
7709  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7710  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7711  * @cfg {String} cls CSS class to add to the element
7712  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7713  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7714  * @constructor
7715  * @param {Object} config An object with config options.
7716  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7717  */
7718
7719 Roo.Layer = function(config, existingEl){
7720     config = config || {};
7721     var dh = Roo.DomHelper;
7722     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7723     if(existingEl){
7724         this.dom = Roo.getDom(existingEl);
7725     }
7726     if(!this.dom){
7727         var o = config.dh || {tag: "div", cls: "x-layer"};
7728         this.dom = dh.append(pel, o);
7729     }
7730     if(config.cls){
7731         this.addClass(config.cls);
7732     }
7733     this.constrain = config.constrain !== false;
7734     this.visibilityMode = Roo.Element.VISIBILITY;
7735     if(config.id){
7736         this.id = this.dom.id = config.id;
7737     }else{
7738         this.id = Roo.id(this.dom);
7739     }
7740     this.zindex = config.zindex || this.getZIndex();
7741     this.position("absolute", this.zindex);
7742     if(config.shadow){
7743         this.shadowOffset = config.shadowOffset || 4;
7744         this.shadow = new Roo.Shadow({
7745             offset : this.shadowOffset,
7746             mode : config.shadow
7747         });
7748     }else{
7749         this.shadowOffset = 0;
7750     }
7751     this.useShim = config.shim !== false && Roo.useShims;
7752     this.useDisplay = config.useDisplay;
7753     this.hide();
7754 };
7755
7756 var supr = Roo.Element.prototype;
7757
7758 // shims are shared among layer to keep from having 100 iframes
7759 var shims = [];
7760
7761 Roo.extend(Roo.Layer, Roo.Element, {
7762
7763     getZIndex : function(){
7764         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7765     },
7766
7767     getShim : function(){
7768         if(!this.useShim){
7769             return null;
7770         }
7771         if(this.shim){
7772             return this.shim;
7773         }
7774         var shim = shims.shift();
7775         if(!shim){
7776             shim = this.createShim();
7777             shim.enableDisplayMode('block');
7778             shim.dom.style.display = 'none';
7779             shim.dom.style.visibility = 'visible';
7780         }
7781         var pn = this.dom.parentNode;
7782         if(shim.dom.parentNode != pn){
7783             pn.insertBefore(shim.dom, this.dom);
7784         }
7785         shim.setStyle('z-index', this.getZIndex()-2);
7786         this.shim = shim;
7787         return shim;
7788     },
7789
7790     hideShim : function(){
7791         if(this.shim){
7792             this.shim.setDisplayed(false);
7793             shims.push(this.shim);
7794             delete this.shim;
7795         }
7796     },
7797
7798     disableShadow : function(){
7799         if(this.shadow){
7800             this.shadowDisabled = true;
7801             this.shadow.hide();
7802             this.lastShadowOffset = this.shadowOffset;
7803             this.shadowOffset = 0;
7804         }
7805     },
7806
7807     enableShadow : function(show){
7808         if(this.shadow){
7809             this.shadowDisabled = false;
7810             this.shadowOffset = this.lastShadowOffset;
7811             delete this.lastShadowOffset;
7812             if(show){
7813                 this.sync(true);
7814             }
7815         }
7816     },
7817
7818     // private
7819     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7820     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7821     sync : function(doShow){
7822         var sw = this.shadow;
7823         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7824             var sh = this.getShim();
7825
7826             var w = this.getWidth(),
7827                 h = this.getHeight();
7828
7829             var l = this.getLeft(true),
7830                 t = this.getTop(true);
7831
7832             if(sw && !this.shadowDisabled){
7833                 if(doShow && !sw.isVisible()){
7834                     sw.show(this);
7835                 }else{
7836                     sw.realign(l, t, w, h);
7837                 }
7838                 if(sh){
7839                     if(doShow){
7840                        sh.show();
7841                     }
7842                     // fit the shim behind the shadow, so it is shimmed too
7843                     var a = sw.adjusts, s = sh.dom.style;
7844                     s.left = (Math.min(l, l+a.l))+"px";
7845                     s.top = (Math.min(t, t+a.t))+"px";
7846                     s.width = (w+a.w)+"px";
7847                     s.height = (h+a.h)+"px";
7848                 }
7849             }else if(sh){
7850                 if(doShow){
7851                    sh.show();
7852                 }
7853                 sh.setSize(w, h);
7854                 sh.setLeftTop(l, t);
7855             }
7856             
7857         }
7858     },
7859
7860     // private
7861     destroy : function(){
7862         this.hideShim();
7863         if(this.shadow){
7864             this.shadow.hide();
7865         }
7866         this.removeAllListeners();
7867         var pn = this.dom.parentNode;
7868         if(pn){
7869             pn.removeChild(this.dom);
7870         }
7871         Roo.Element.uncache(this.id);
7872     },
7873
7874     remove : function(){
7875         this.destroy();
7876     },
7877
7878     // private
7879     beginUpdate : function(){
7880         this.updating = true;
7881     },
7882
7883     // private
7884     endUpdate : function(){
7885         this.updating = false;
7886         this.sync(true);
7887     },
7888
7889     // private
7890     hideUnders : function(negOffset){
7891         if(this.shadow){
7892             this.shadow.hide();
7893         }
7894         this.hideShim();
7895     },
7896
7897     // private
7898     constrainXY : function(){
7899         if(this.constrain){
7900             var vw = Roo.lib.Dom.getViewWidth(),
7901                 vh = Roo.lib.Dom.getViewHeight();
7902             var s = Roo.get(document).getScroll();
7903
7904             var xy = this.getXY();
7905             var x = xy[0], y = xy[1];   
7906             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7907             // only move it if it needs it
7908             var moved = false;
7909             // first validate right/bottom
7910             if((x + w) > vw+s.left){
7911                 x = vw - w - this.shadowOffset;
7912                 moved = true;
7913             }
7914             if((y + h) > vh+s.top){
7915                 y = vh - h - this.shadowOffset;
7916                 moved = true;
7917             }
7918             // then make sure top/left isn't negative
7919             if(x < s.left){
7920                 x = s.left;
7921                 moved = true;
7922             }
7923             if(y < s.top){
7924                 y = s.top;
7925                 moved = true;
7926             }
7927             if(moved){
7928                 if(this.avoidY){
7929                     var ay = this.avoidY;
7930                     if(y <= ay && (y+h) >= ay){
7931                         y = ay-h-5;   
7932                     }
7933                 }
7934                 xy = [x, y];
7935                 this.storeXY(xy);
7936                 supr.setXY.call(this, xy);
7937                 this.sync();
7938             }
7939         }
7940     },
7941
7942     isVisible : function(){
7943         return this.visible;    
7944     },
7945
7946     // private
7947     showAction : function(){
7948         this.visible = true; // track visibility to prevent getStyle calls
7949         if(this.useDisplay === true){
7950             this.setDisplayed("");
7951         }else if(this.lastXY){
7952             supr.setXY.call(this, this.lastXY);
7953         }else if(this.lastLT){
7954             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7955         }
7956     },
7957
7958     // private
7959     hideAction : function(){
7960         this.visible = false;
7961         if(this.useDisplay === true){
7962             this.setDisplayed(false);
7963         }else{
7964             this.setLeftTop(-10000,-10000);
7965         }
7966     },
7967
7968     // overridden Element method
7969     setVisible : function(v, a, d, c, e){
7970         if(v){
7971             this.showAction();
7972         }
7973         if(a && v){
7974             var cb = function(){
7975                 this.sync(true);
7976                 if(c){
7977                     c();
7978                 }
7979             }.createDelegate(this);
7980             supr.setVisible.call(this, true, true, d, cb, e);
7981         }else{
7982             if(!v){
7983                 this.hideUnders(true);
7984             }
7985             var cb = c;
7986             if(a){
7987                 cb = function(){
7988                     this.hideAction();
7989                     if(c){
7990                         c();
7991                     }
7992                 }.createDelegate(this);
7993             }
7994             supr.setVisible.call(this, v, a, d, cb, e);
7995             if(v){
7996                 this.sync(true);
7997             }else if(!a){
7998                 this.hideAction();
7999             }
8000         }
8001     },
8002
8003     storeXY : function(xy){
8004         delete this.lastLT;
8005         this.lastXY = xy;
8006     },
8007
8008     storeLeftTop : function(left, top){
8009         delete this.lastXY;
8010         this.lastLT = [left, top];
8011     },
8012
8013     // private
8014     beforeFx : function(){
8015         this.beforeAction();
8016         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
8017     },
8018
8019     // private
8020     afterFx : function(){
8021         Roo.Layer.superclass.afterFx.apply(this, arguments);
8022         this.sync(this.isVisible());
8023     },
8024
8025     // private
8026     beforeAction : function(){
8027         if(!this.updating && this.shadow){
8028             this.shadow.hide();
8029         }
8030     },
8031
8032     // overridden Element method
8033     setLeft : function(left){
8034         this.storeLeftTop(left, this.getTop(true));
8035         supr.setLeft.apply(this, arguments);
8036         this.sync();
8037     },
8038
8039     setTop : function(top){
8040         this.storeLeftTop(this.getLeft(true), top);
8041         supr.setTop.apply(this, arguments);
8042         this.sync();
8043     },
8044
8045     setLeftTop : function(left, top){
8046         this.storeLeftTop(left, top);
8047         supr.setLeftTop.apply(this, arguments);
8048         this.sync();
8049     },
8050
8051     setXY : function(xy, a, d, c, e){
8052         this.fixDisplay();
8053         this.beforeAction();
8054         this.storeXY(xy);
8055         var cb = this.createCB(c);
8056         supr.setXY.call(this, xy, a, d, cb, e);
8057         if(!a){
8058             cb();
8059         }
8060     },
8061
8062     // private
8063     createCB : function(c){
8064         var el = this;
8065         return function(){
8066             el.constrainXY();
8067             el.sync(true);
8068             if(c){
8069                 c();
8070             }
8071         };
8072     },
8073
8074     // overridden Element method
8075     setX : function(x, a, d, c, e){
8076         this.setXY([x, this.getY()], a, d, c, e);
8077     },
8078
8079     // overridden Element method
8080     setY : function(y, a, d, c, e){
8081         this.setXY([this.getX(), y], a, d, c, e);
8082     },
8083
8084     // overridden Element method
8085     setSize : function(w, h, a, d, c, e){
8086         this.beforeAction();
8087         var cb = this.createCB(c);
8088         supr.setSize.call(this, w, h, a, d, cb, e);
8089         if(!a){
8090             cb();
8091         }
8092     },
8093
8094     // overridden Element method
8095     setWidth : function(w, a, d, c, e){
8096         this.beforeAction();
8097         var cb = this.createCB(c);
8098         supr.setWidth.call(this, w, a, d, cb, e);
8099         if(!a){
8100             cb();
8101         }
8102     },
8103
8104     // overridden Element method
8105     setHeight : function(h, a, d, c, e){
8106         this.beforeAction();
8107         var cb = this.createCB(c);
8108         supr.setHeight.call(this, h, a, d, cb, e);
8109         if(!a){
8110             cb();
8111         }
8112     },
8113
8114     // overridden Element method
8115     setBounds : function(x, y, w, h, a, d, c, e){
8116         this.beforeAction();
8117         var cb = this.createCB(c);
8118         if(!a){
8119             this.storeXY([x, y]);
8120             supr.setXY.call(this, [x, y]);
8121             supr.setSize.call(this, w, h, a, d, cb, e);
8122             cb();
8123         }else{
8124             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8125         }
8126         return this;
8127     },
8128     
8129     /**
8130      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8131      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8132      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8133      * @param {Number} zindex The new z-index to set
8134      * @return {this} The Layer
8135      */
8136     setZIndex : function(zindex){
8137         this.zindex = zindex;
8138         this.setStyle("z-index", zindex + 2);
8139         if(this.shadow){
8140             this.shadow.setZIndex(zindex + 1);
8141         }
8142         if(this.shim){
8143             this.shim.setStyle("z-index", zindex);
8144         }
8145     }
8146 });
8147 })();/*
8148  * Based on:
8149  * Ext JS Library 1.1.1
8150  * Copyright(c) 2006-2007, Ext JS, LLC.
8151  *
8152  * Originally Released Under LGPL - original licence link has changed is not relivant.
8153  *
8154  * Fork - LGPL
8155  * <script type="text/javascript">
8156  */
8157
8158
8159 /**
8160  * @class Roo.Shadow
8161  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8162  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8163  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8164  * @constructor
8165  * Create a new Shadow
8166  * @param {Object} config The config object
8167  */
8168 Roo.Shadow = function(config){
8169     Roo.apply(this, config);
8170     if(typeof this.mode != "string"){
8171         this.mode = this.defaultMode;
8172     }
8173     var o = this.offset, a = {h: 0};
8174     var rad = Math.floor(this.offset/2);
8175     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8176         case "drop":
8177             a.w = 0;
8178             a.l = a.t = o;
8179             a.t -= 1;
8180             if(Roo.isIE){
8181                 a.l -= this.offset + rad;
8182                 a.t -= this.offset + rad;
8183                 a.w -= rad;
8184                 a.h -= rad;
8185                 a.t += 1;
8186             }
8187         break;
8188         case "sides":
8189             a.w = (o*2);
8190             a.l = -o;
8191             a.t = o-1;
8192             if(Roo.isIE){
8193                 a.l -= (this.offset - rad);
8194                 a.t -= this.offset + rad;
8195                 a.l += 1;
8196                 a.w -= (this.offset - rad)*2;
8197                 a.w -= rad + 1;
8198                 a.h -= 1;
8199             }
8200         break;
8201         case "frame":
8202             a.w = a.h = (o*2);
8203             a.l = a.t = -o;
8204             a.t += 1;
8205             a.h -= 2;
8206             if(Roo.isIE){
8207                 a.l -= (this.offset - rad);
8208                 a.t -= (this.offset - rad);
8209                 a.l += 1;
8210                 a.w -= (this.offset + rad + 1);
8211                 a.h -= (this.offset + rad);
8212                 a.h += 1;
8213             }
8214         break;
8215     };
8216
8217     this.adjusts = a;
8218 };
8219
8220 Roo.Shadow.prototype = {
8221     /**
8222      * @cfg {String} mode
8223      * The shadow display mode.  Supports the following options:<br />
8224      * sides: Shadow displays on both sides and bottom only<br />
8225      * frame: Shadow displays equally on all four sides<br />
8226      * drop: Traditional bottom-right drop shadow (default)
8227      */
8228     /**
8229      * @cfg {String} offset
8230      * The number of pixels to offset the shadow from the element (defaults to 4)
8231      */
8232     offset: 4,
8233
8234     // private
8235     defaultMode: "drop",
8236
8237     /**
8238      * Displays the shadow under the target element
8239      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8240      */
8241     show : function(target){
8242         target = Roo.get(target);
8243         if(!this.el){
8244             this.el = Roo.Shadow.Pool.pull();
8245             if(this.el.dom.nextSibling != target.dom){
8246                 this.el.insertBefore(target);
8247             }
8248         }
8249         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8250         if(Roo.isIE){
8251             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8252         }
8253         this.realign(
8254             target.getLeft(true),
8255             target.getTop(true),
8256             target.getWidth(),
8257             target.getHeight()
8258         );
8259         this.el.dom.style.display = "block";
8260     },
8261
8262     /**
8263      * Returns true if the shadow is visible, else false
8264      */
8265     isVisible : function(){
8266         return this.el ? true : false;  
8267     },
8268
8269     /**
8270      * Direct alignment when values are already available. Show must be called at least once before
8271      * calling this method to ensure it is initialized.
8272      * @param {Number} left The target element left position
8273      * @param {Number} top The target element top position
8274      * @param {Number} width The target element width
8275      * @param {Number} height The target element height
8276      */
8277     realign : function(l, t, w, h){
8278         if(!this.el){
8279             return;
8280         }
8281         var a = this.adjusts, d = this.el.dom, s = d.style;
8282         var iea = 0;
8283         s.left = (l+a.l)+"px";
8284         s.top = (t+a.t)+"px";
8285         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8286  
8287         if(s.width != sws || s.height != shs){
8288             s.width = sws;
8289             s.height = shs;
8290             if(!Roo.isIE){
8291                 var cn = d.childNodes;
8292                 var sww = Math.max(0, (sw-12))+"px";
8293                 cn[0].childNodes[1].style.width = sww;
8294                 cn[1].childNodes[1].style.width = sww;
8295                 cn[2].childNodes[1].style.width = sww;
8296                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8297             }
8298         }
8299     },
8300
8301     /**
8302      * Hides this shadow
8303      */
8304     hide : function(){
8305         if(this.el){
8306             this.el.dom.style.display = "none";
8307             Roo.Shadow.Pool.push(this.el);
8308             delete this.el;
8309         }
8310     },
8311
8312     /**
8313      * Adjust the z-index of this shadow
8314      * @param {Number} zindex The new z-index
8315      */
8316     setZIndex : function(z){
8317         this.zIndex = z;
8318         if(this.el){
8319             this.el.setStyle("z-index", z);
8320         }
8321     }
8322 };
8323
8324 // Private utility class that manages the internal Shadow cache
8325 Roo.Shadow.Pool = function(){
8326     var p = [];
8327     var markup = Roo.isIE ?
8328                  '<div class="x-ie-shadow"></div>' :
8329                  '<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>';
8330     return {
8331         pull : function(){
8332             var sh = p.shift();
8333             if(!sh){
8334                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8335                 sh.autoBoxAdjust = false;
8336             }
8337             return sh;
8338         },
8339
8340         push : function(sh){
8341             p.push(sh);
8342         }
8343     };
8344 }();/*
8345  * Based on:
8346  * Ext JS Library 1.1.1
8347  * Copyright(c) 2006-2007, Ext JS, LLC.
8348  *
8349  * Originally Released Under LGPL - original licence link has changed is not relivant.
8350  *
8351  * Fork - LGPL
8352  * <script type="text/javascript">
8353  */
8354
8355 /**
8356  * @class Roo.BoxComponent
8357  * @extends Roo.Component
8358  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8359  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8360  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8361  * layout containers.
8362  * @constructor
8363  * @param {Roo.Element/String/Object} config The configuration options.
8364  */
8365 Roo.BoxComponent = function(config){
8366     Roo.Component.call(this, config);
8367     this.addEvents({
8368         /**
8369          * @event resize
8370          * Fires after the component is resized.
8371              * @param {Roo.Component} this
8372              * @param {Number} adjWidth The box-adjusted width that was set
8373              * @param {Number} adjHeight The box-adjusted height that was set
8374              * @param {Number} rawWidth The width that was originally specified
8375              * @param {Number} rawHeight The height that was originally specified
8376              */
8377         resize : true,
8378         /**
8379          * @event move
8380          * Fires after the component is moved.
8381              * @param {Roo.Component} this
8382              * @param {Number} x The new x position
8383              * @param {Number} y The new y position
8384              */
8385         move : true
8386     });
8387 };
8388
8389 Roo.extend(Roo.BoxComponent, Roo.Component, {
8390     // private, set in afterRender to signify that the component has been rendered
8391     boxReady : false,
8392     // private, used to defer height settings to subclasses
8393     deferHeight: false,
8394     /** @cfg {Number} width
8395      * width (optional) size of component
8396      */
8397      /** @cfg {Number} height
8398      * height (optional) size of component
8399      */
8400      
8401     /**
8402      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8403      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8404      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8405      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8406      * @return {Roo.BoxComponent} this
8407      */
8408     setSize : function(w, h){
8409         // support for standard size objects
8410         if(typeof w == 'object'){
8411             h = w.height;
8412             w = w.width;
8413         }
8414         // not rendered
8415         if(!this.boxReady){
8416             this.width = w;
8417             this.height = h;
8418             return this;
8419         }
8420
8421         // prevent recalcs when not needed
8422         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8423             return this;
8424         }
8425         this.lastSize = {width: w, height: h};
8426
8427         var adj = this.adjustSize(w, h);
8428         var aw = adj.width, ah = adj.height;
8429         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8430             var rz = this.getResizeEl();
8431             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8432                 rz.setSize(aw, ah);
8433             }else if(!this.deferHeight && ah !== undefined){
8434                 rz.setHeight(ah);
8435             }else if(aw !== undefined){
8436                 rz.setWidth(aw);
8437             }
8438             this.onResize(aw, ah, w, h);
8439             this.fireEvent('resize', this, aw, ah, w, h);
8440         }
8441         return this;
8442     },
8443
8444     /**
8445      * Gets the current size of the component's underlying element.
8446      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8447      */
8448     getSize : function(){
8449         return this.el.getSize();
8450     },
8451
8452     /**
8453      * Gets the current XY position of the component's underlying element.
8454      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8455      * @return {Array} The XY position of the element (e.g., [100, 200])
8456      */
8457     getPosition : function(local){
8458         if(local === true){
8459             return [this.el.getLeft(true), this.el.getTop(true)];
8460         }
8461         return this.xy || this.el.getXY();
8462     },
8463
8464     /**
8465      * Gets the current box measurements of the component's underlying element.
8466      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8467      * @returns {Object} box An object in the format {x, y, width, height}
8468      */
8469     getBox : function(local){
8470         var s = this.el.getSize();
8471         if(local){
8472             s.x = this.el.getLeft(true);
8473             s.y = this.el.getTop(true);
8474         }else{
8475             var xy = this.xy || this.el.getXY();
8476             s.x = xy[0];
8477             s.y = xy[1];
8478         }
8479         return s;
8480     },
8481
8482     /**
8483      * Sets the current box measurements of the component's underlying element.
8484      * @param {Object} box An object in the format {x, y, width, height}
8485      * @returns {Roo.BoxComponent} this
8486      */
8487     updateBox : function(box){
8488         this.setSize(box.width, box.height);
8489         this.setPagePosition(box.x, box.y);
8490         return this;
8491     },
8492
8493     // protected
8494     getResizeEl : function(){
8495         return this.resizeEl || this.el;
8496     },
8497
8498     // protected
8499     getPositionEl : function(){
8500         return this.positionEl || this.el;
8501     },
8502
8503     /**
8504      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8505      * This method fires the move event.
8506      * @param {Number} left The new left
8507      * @param {Number} top The new top
8508      * @returns {Roo.BoxComponent} this
8509      */
8510     setPosition : function(x, y){
8511         this.x = x;
8512         this.y = y;
8513         if(!this.boxReady){
8514             return this;
8515         }
8516         var adj = this.adjustPosition(x, y);
8517         var ax = adj.x, ay = adj.y;
8518
8519         var el = this.getPositionEl();
8520         if(ax !== undefined || ay !== undefined){
8521             if(ax !== undefined && ay !== undefined){
8522                 el.setLeftTop(ax, ay);
8523             }else if(ax !== undefined){
8524                 el.setLeft(ax);
8525             }else if(ay !== undefined){
8526                 el.setTop(ay);
8527             }
8528             this.onPosition(ax, ay);
8529             this.fireEvent('move', this, ax, ay);
8530         }
8531         return this;
8532     },
8533
8534     /**
8535      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8536      * This method fires the move event.
8537      * @param {Number} x The new x position
8538      * @param {Number} y The new y position
8539      * @returns {Roo.BoxComponent} this
8540      */
8541     setPagePosition : function(x, y){
8542         this.pageX = x;
8543         this.pageY = y;
8544         if(!this.boxReady){
8545             return;
8546         }
8547         if(x === undefined || y === undefined){ // cannot translate undefined points
8548             return;
8549         }
8550         var p = this.el.translatePoints(x, y);
8551         this.setPosition(p.left, p.top);
8552         return this;
8553     },
8554
8555     // private
8556     onRender : function(ct, position){
8557         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8558         if(this.resizeEl){
8559             this.resizeEl = Roo.get(this.resizeEl);
8560         }
8561         if(this.positionEl){
8562             this.positionEl = Roo.get(this.positionEl);
8563         }
8564     },
8565
8566     // private
8567     afterRender : function(){
8568         Roo.BoxComponent.superclass.afterRender.call(this);
8569         this.boxReady = true;
8570         this.setSize(this.width, this.height);
8571         if(this.x || this.y){
8572             this.setPosition(this.x, this.y);
8573         }
8574         if(this.pageX || this.pageY){
8575             this.setPagePosition(this.pageX, this.pageY);
8576         }
8577     },
8578
8579     /**
8580      * Force the component's size to recalculate based on the underlying element's current height and width.
8581      * @returns {Roo.BoxComponent} this
8582      */
8583     syncSize : function(){
8584         delete this.lastSize;
8585         this.setSize(this.el.getWidth(), this.el.getHeight());
8586         return this;
8587     },
8588
8589     /**
8590      * Called after the component is resized, this method is empty by default but can be implemented by any
8591      * subclass that needs to perform custom logic after a resize occurs.
8592      * @param {Number} adjWidth The box-adjusted width that was set
8593      * @param {Number} adjHeight The box-adjusted height that was set
8594      * @param {Number} rawWidth The width that was originally specified
8595      * @param {Number} rawHeight The height that was originally specified
8596      */
8597     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8598
8599     },
8600
8601     /**
8602      * Called after the component is moved, this method is empty by default but can be implemented by any
8603      * subclass that needs to perform custom logic after a move occurs.
8604      * @param {Number} x The new x position
8605      * @param {Number} y The new y position
8606      */
8607     onPosition : function(x, y){
8608
8609     },
8610
8611     // private
8612     adjustSize : function(w, h){
8613         if(this.autoWidth){
8614             w = 'auto';
8615         }
8616         if(this.autoHeight){
8617             h = 'auto';
8618         }
8619         return {width : w, height: h};
8620     },
8621
8622     // private
8623     adjustPosition : function(x, y){
8624         return {x : x, y: y};
8625     }
8626 });/*
8627  * Based on:
8628  * Ext JS Library 1.1.1
8629  * Copyright(c) 2006-2007, Ext JS, LLC.
8630  *
8631  * Originally Released Under LGPL - original licence link has changed is not relivant.
8632  *
8633  * Fork - LGPL
8634  * <script type="text/javascript">
8635  */
8636
8637
8638 /**
8639  * @class Roo.SplitBar
8640  * @extends Roo.util.Observable
8641  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8642  * <br><br>
8643  * Usage:
8644  * <pre><code>
8645 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8646                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8647 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8648 split.minSize = 100;
8649 split.maxSize = 600;
8650 split.animate = true;
8651 split.on('moved', splitterMoved);
8652 </code></pre>
8653  * @constructor
8654  * Create a new SplitBar
8655  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8656  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8657  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8658  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8659                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8660                         position of the SplitBar).
8661  */
8662 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8663     
8664     /** @private */
8665     this.el = Roo.get(dragElement, true);
8666     this.el.dom.unselectable = "on";
8667     /** @private */
8668     this.resizingEl = Roo.get(resizingElement, true);
8669
8670     /**
8671      * @private
8672      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8673      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8674      * @type Number
8675      */
8676     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8677     
8678     /**
8679      * The minimum size of the resizing element. (Defaults to 0)
8680      * @type Number
8681      */
8682     this.minSize = 0;
8683     
8684     /**
8685      * The maximum size of the resizing element. (Defaults to 2000)
8686      * @type Number
8687      */
8688     this.maxSize = 2000;
8689     
8690     /**
8691      * Whether to animate the transition to the new size
8692      * @type Boolean
8693      */
8694     this.animate = false;
8695     
8696     /**
8697      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8698      * @type Boolean
8699      */
8700     this.useShim = false;
8701     
8702     /** @private */
8703     this.shim = null;
8704     
8705     if(!existingProxy){
8706         /** @private */
8707         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8708     }else{
8709         this.proxy = Roo.get(existingProxy).dom;
8710     }
8711     /** @private */
8712     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8713     
8714     /** @private */
8715     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8716     
8717     /** @private */
8718     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8719     
8720     /** @private */
8721     this.dragSpecs = {};
8722     
8723     /**
8724      * @private The adapter to use to positon and resize elements
8725      */
8726     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8727     this.adapter.init(this);
8728     
8729     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8730         /** @private */
8731         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8732         this.el.addClass("x-splitbar-h");
8733     }else{
8734         /** @private */
8735         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8736         this.el.addClass("x-splitbar-v");
8737     }
8738     
8739     this.addEvents({
8740         /**
8741          * @event resize
8742          * Fires when the splitter is moved (alias for {@link #event-moved})
8743          * @param {Roo.SplitBar} this
8744          * @param {Number} newSize the new width or height
8745          */
8746         "resize" : true,
8747         /**
8748          * @event moved
8749          * Fires when the splitter is moved
8750          * @param {Roo.SplitBar} this
8751          * @param {Number} newSize the new width or height
8752          */
8753         "moved" : true,
8754         /**
8755          * @event beforeresize
8756          * Fires before the splitter is dragged
8757          * @param {Roo.SplitBar} this
8758          */
8759         "beforeresize" : true,
8760
8761         "beforeapply" : true
8762     });
8763
8764     Roo.util.Observable.call(this);
8765 };
8766
8767 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8768     onStartProxyDrag : function(x, y){
8769         this.fireEvent("beforeresize", this);
8770         if(!this.overlay){
8771             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8772             o.unselectable();
8773             o.enableDisplayMode("block");
8774             // all splitbars share the same overlay
8775             Roo.SplitBar.prototype.overlay = o;
8776         }
8777         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8778         this.overlay.show();
8779         Roo.get(this.proxy).setDisplayed("block");
8780         var size = this.adapter.getElementSize(this);
8781         this.activeMinSize = this.getMinimumSize();;
8782         this.activeMaxSize = this.getMaximumSize();;
8783         var c1 = size - this.activeMinSize;
8784         var c2 = Math.max(this.activeMaxSize - size, 0);
8785         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8786             this.dd.resetConstraints();
8787             this.dd.setXConstraint(
8788                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8789                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8790             );
8791             this.dd.setYConstraint(0, 0);
8792         }else{
8793             this.dd.resetConstraints();
8794             this.dd.setXConstraint(0, 0);
8795             this.dd.setYConstraint(
8796                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8797                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8798             );
8799          }
8800         this.dragSpecs.startSize = size;
8801         this.dragSpecs.startPoint = [x, y];
8802         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8803     },
8804     
8805     /** 
8806      * @private Called after the drag operation by the DDProxy
8807      */
8808     onEndProxyDrag : function(e){
8809         Roo.get(this.proxy).setDisplayed(false);
8810         var endPoint = Roo.lib.Event.getXY(e);
8811         if(this.overlay){
8812             this.overlay.hide();
8813         }
8814         var newSize;
8815         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8816             newSize = this.dragSpecs.startSize + 
8817                 (this.placement == Roo.SplitBar.LEFT ?
8818                     endPoint[0] - this.dragSpecs.startPoint[0] :
8819                     this.dragSpecs.startPoint[0] - endPoint[0]
8820                 );
8821         }else{
8822             newSize = this.dragSpecs.startSize + 
8823                 (this.placement == Roo.SplitBar.TOP ?
8824                     endPoint[1] - this.dragSpecs.startPoint[1] :
8825                     this.dragSpecs.startPoint[1] - endPoint[1]
8826                 );
8827         }
8828         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8829         if(newSize != this.dragSpecs.startSize){
8830             if(this.fireEvent('beforeapply', this, newSize) !== false){
8831                 this.adapter.setElementSize(this, newSize);
8832                 this.fireEvent("moved", this, newSize);
8833                 this.fireEvent("resize", this, newSize);
8834             }
8835         }
8836     },
8837     
8838     /**
8839      * Get the adapter this SplitBar uses
8840      * @return The adapter object
8841      */
8842     getAdapter : function(){
8843         return this.adapter;
8844     },
8845     
8846     /**
8847      * Set the adapter this SplitBar uses
8848      * @param {Object} adapter A SplitBar adapter object
8849      */
8850     setAdapter : function(adapter){
8851         this.adapter = adapter;
8852         this.adapter.init(this);
8853     },
8854     
8855     /**
8856      * Gets the minimum size for the resizing element
8857      * @return {Number} The minimum size
8858      */
8859     getMinimumSize : function(){
8860         return this.minSize;
8861     },
8862     
8863     /**
8864      * Sets the minimum size for the resizing element
8865      * @param {Number} minSize The minimum size
8866      */
8867     setMinimumSize : function(minSize){
8868         this.minSize = minSize;
8869     },
8870     
8871     /**
8872      * Gets the maximum size for the resizing element
8873      * @return {Number} The maximum size
8874      */
8875     getMaximumSize : function(){
8876         return this.maxSize;
8877     },
8878     
8879     /**
8880      * Sets the maximum size for the resizing element
8881      * @param {Number} maxSize The maximum size
8882      */
8883     setMaximumSize : function(maxSize){
8884         this.maxSize = maxSize;
8885     },
8886     
8887     /**
8888      * Sets the initialize size for the resizing element
8889      * @param {Number} size The initial size
8890      */
8891     setCurrentSize : function(size){
8892         var oldAnimate = this.animate;
8893         this.animate = false;
8894         this.adapter.setElementSize(this, size);
8895         this.animate = oldAnimate;
8896     },
8897     
8898     /**
8899      * Destroy this splitbar. 
8900      * @param {Boolean} removeEl True to remove the element
8901      */
8902     destroy : function(removeEl){
8903         if(this.shim){
8904             this.shim.remove();
8905         }
8906         this.dd.unreg();
8907         this.proxy.parentNode.removeChild(this.proxy);
8908         if(removeEl){
8909             this.el.remove();
8910         }
8911     }
8912 });
8913
8914 /**
8915  * @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.
8916  */
8917 Roo.SplitBar.createProxy = function(dir){
8918     var proxy = new Roo.Element(document.createElement("div"));
8919     proxy.unselectable();
8920     var cls = 'x-splitbar-proxy';
8921     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8922     document.body.appendChild(proxy.dom);
8923     return proxy.dom;
8924 };
8925
8926 /** 
8927  * @class Roo.SplitBar.BasicLayoutAdapter
8928  * Default Adapter. It assumes the splitter and resizing element are not positioned
8929  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8930  */
8931 Roo.SplitBar.BasicLayoutAdapter = function(){
8932 };
8933
8934 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8935     // do nothing for now
8936     init : function(s){
8937     
8938     },
8939     /**
8940      * Called before drag operations to get the current size of the resizing element. 
8941      * @param {Roo.SplitBar} s The SplitBar using this adapter
8942      */
8943      getElementSize : function(s){
8944         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8945             return s.resizingEl.getWidth();
8946         }else{
8947             return s.resizingEl.getHeight();
8948         }
8949     },
8950     
8951     /**
8952      * Called after drag operations to set the size of the resizing element.
8953      * @param {Roo.SplitBar} s The SplitBar using this adapter
8954      * @param {Number} newSize The new size to set
8955      * @param {Function} onComplete A function to be invoked when resizing is complete
8956      */
8957     setElementSize : function(s, newSize, onComplete){
8958         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8959             if(!s.animate){
8960                 s.resizingEl.setWidth(newSize);
8961                 if(onComplete){
8962                     onComplete(s, newSize);
8963                 }
8964             }else{
8965                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8966             }
8967         }else{
8968             
8969             if(!s.animate){
8970                 s.resizingEl.setHeight(newSize);
8971                 if(onComplete){
8972                     onComplete(s, newSize);
8973                 }
8974             }else{
8975                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
8976             }
8977         }
8978     }
8979 };
8980
8981 /** 
8982  *@class Roo.SplitBar.AbsoluteLayoutAdapter
8983  * @extends Roo.SplitBar.BasicLayoutAdapter
8984  * Adapter that  moves the splitter element to align with the resized sizing element. 
8985  * Used with an absolute positioned SplitBar.
8986  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
8987  * document.body, make sure you assign an id to the body element.
8988  */
8989 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
8990     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
8991     this.container = Roo.get(container);
8992 };
8993
8994 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
8995     init : function(s){
8996         this.basic.init(s);
8997     },
8998     
8999     getElementSize : function(s){
9000         return this.basic.getElementSize(s);
9001     },
9002     
9003     setElementSize : function(s, newSize, onComplete){
9004         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
9005     },
9006     
9007     moveSplitter : function(s){
9008         var yes = Roo.SplitBar;
9009         switch(s.placement){
9010             case yes.LEFT:
9011                 s.el.setX(s.resizingEl.getRight());
9012                 break;
9013             case yes.RIGHT:
9014                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
9015                 break;
9016             case yes.TOP:
9017                 s.el.setY(s.resizingEl.getBottom());
9018                 break;
9019             case yes.BOTTOM:
9020                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
9021                 break;
9022         }
9023     }
9024 };
9025
9026 /**
9027  * Orientation constant - Create a vertical SplitBar
9028  * @static
9029  * @type Number
9030  */
9031 Roo.SplitBar.VERTICAL = 1;
9032
9033 /**
9034  * Orientation constant - Create a horizontal SplitBar
9035  * @static
9036  * @type Number
9037  */
9038 Roo.SplitBar.HORIZONTAL = 2;
9039
9040 /**
9041  * Placement constant - The resizing element is to the left of the splitter element
9042  * @static
9043  * @type Number
9044  */
9045 Roo.SplitBar.LEFT = 1;
9046
9047 /**
9048  * Placement constant - The resizing element is to the right of the splitter element
9049  * @static
9050  * @type Number
9051  */
9052 Roo.SplitBar.RIGHT = 2;
9053
9054 /**
9055  * Placement constant - The resizing element is positioned above the splitter element
9056  * @static
9057  * @type Number
9058  */
9059 Roo.SplitBar.TOP = 3;
9060
9061 /**
9062  * Placement constant - The resizing element is positioned under splitter element
9063  * @static
9064  * @type Number
9065  */
9066 Roo.SplitBar.BOTTOM = 4;
9067 /*
9068  * Based on:
9069  * Ext JS Library 1.1.1
9070  * Copyright(c) 2006-2007, Ext JS, LLC.
9071  *
9072  * Originally Released Under LGPL - original licence link has changed is not relivant.
9073  *
9074  * Fork - LGPL
9075  * <script type="text/javascript">
9076  */
9077
9078 /**
9079  * @class Roo.View
9080  * @extends Roo.util.Observable
9081  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9082  * This class also supports single and multi selection modes. <br>
9083  * Create a data model bound view:
9084  <pre><code>
9085  var store = new Roo.data.Store(...);
9086
9087  var view = new Roo.View({
9088     el : "my-element",
9089     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9090  
9091     singleSelect: true,
9092     selectedClass: "ydataview-selected",
9093     store: store
9094  });
9095
9096  // listen for node click?
9097  view.on("click", function(vw, index, node, e){
9098  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9099  });
9100
9101  // load XML data
9102  dataModel.load("foobar.xml");
9103  </code></pre>
9104  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9105  * <br><br>
9106  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9107  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9108  * 
9109  * Note: old style constructor is still suported (container, template, config)
9110  * 
9111  * @constructor
9112  * Create a new View
9113  * @param {Object} config The config object
9114  * 
9115  */
9116 Roo.View = function(config, depreciated_tpl, depreciated_config){
9117     
9118     if (typeof(depreciated_tpl) == 'undefined') {
9119         // new way.. - universal constructor.
9120         Roo.apply(this, config);
9121         this.el  = Roo.get(this.el);
9122     } else {
9123         // old format..
9124         this.el  = Roo.get(config);
9125         this.tpl = depreciated_tpl;
9126         Roo.apply(this, depreciated_config);
9127     }
9128      
9129     
9130     if(typeof(this.tpl) == "string"){
9131         this.tpl = new Roo.Template(this.tpl);
9132     } else {
9133         // support xtype ctors..
9134         this.tpl = new Roo.factory(this.tpl, Roo);
9135     }
9136     
9137     
9138     this.tpl.compile();
9139    
9140
9141      
9142     /** @private */
9143     this.addEvents({
9144     /**
9145      * @event beforeclick
9146      * Fires before a click is processed. Returns false to cancel the default action.
9147      * @param {Roo.View} this
9148      * @param {Number} index The index of the target node
9149      * @param {HTMLElement} node The target node
9150      * @param {Roo.EventObject} e The raw event object
9151      */
9152         "beforeclick" : true,
9153     /**
9154      * @event click
9155      * Fires when a template node is clicked.
9156      * @param {Roo.View} this
9157      * @param {Number} index The index of the target node
9158      * @param {HTMLElement} node The target node
9159      * @param {Roo.EventObject} e The raw event object
9160      */
9161         "click" : true,
9162     /**
9163      * @event dblclick
9164      * Fires when a template node is double clicked.
9165      * @param {Roo.View} this
9166      * @param {Number} index The index of the target node
9167      * @param {HTMLElement} node The target node
9168      * @param {Roo.EventObject} e The raw event object
9169      */
9170         "dblclick" : true,
9171     /**
9172      * @event contextmenu
9173      * Fires when a template node is right clicked.
9174      * @param {Roo.View} this
9175      * @param {Number} index The index of the target node
9176      * @param {HTMLElement} node The target node
9177      * @param {Roo.EventObject} e The raw event object
9178      */
9179         "contextmenu" : true,
9180     /**
9181      * @event selectionchange
9182      * Fires when the selected nodes change.
9183      * @param {Roo.View} this
9184      * @param {Array} selections Array of the selected nodes
9185      */
9186         "selectionchange" : true,
9187
9188     /**
9189      * @event beforeselect
9190      * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9191      * @param {Roo.View} this
9192      * @param {HTMLElement} node The node to be selected
9193      * @param {Array} selections Array of currently selected nodes
9194      */
9195         "beforeselect" : true
9196     });
9197
9198     this.el.on({
9199         "click": this.onClick,
9200         "dblclick": this.onDblClick,
9201         "contextmenu": this.onContextMenu,
9202         scope:this
9203     });
9204
9205     this.selections = [];
9206     this.nodes = [];
9207     this.cmp = new Roo.CompositeElementLite([]);
9208     if(this.store){
9209         this.store = Roo.factory(this.store, Roo.data);
9210         this.setStore(this.store, true);
9211     }
9212     Roo.View.superclass.constructor.call(this);
9213 };
9214
9215 Roo.extend(Roo.View, Roo.util.Observable, {
9216     
9217      /**
9218      * @cfg {Roo.data.Store} store Data store to load data from.
9219      */
9220     store : false,
9221     
9222     /**
9223      * @cfg {String|Roo.Element} el The container element.
9224      */
9225     el : '',
9226     
9227     /**
9228      * @cfg {String|Roo.Template} tpl The template used by this View 
9229      */
9230     tpl : false,
9231     
9232     /**
9233      * @cfg {String} selectedClass The css class to add to selected nodes
9234      */
9235     selectedClass : "x-view-selected",
9236      /**
9237      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9238      */
9239     emptyText : "",
9240     /**
9241      * @cfg {Boolean} multiSelect Allow multiple selection
9242      */
9243     
9244     multiSelect : false,
9245     /**
9246      * @cfg {Boolean} singleSelect Allow single selection
9247      */
9248     singleSelect:  false,
9249     
9250     /**
9251      * Returns the element this view is bound to.
9252      * @return {Roo.Element}
9253      */
9254     getEl : function(){
9255         return this.el;
9256     },
9257
9258     /**
9259      * Refreshes the view.
9260      */
9261     refresh : function(){
9262         var t = this.tpl;
9263         this.clearSelections();
9264         this.el.update("");
9265         var html = [];
9266         var records = this.store.getRange();
9267         if(records.length < 1){
9268             this.el.update(this.emptyText);
9269             return;
9270         }
9271         for(var i = 0, len = records.length; i < len; i++){
9272             var data = this.prepareData(records[i].data, i, records[i]);
9273             html[html.length] = t.apply(data);
9274         }
9275         this.el.update(html.join(""));
9276         this.nodes = this.el.dom.childNodes;
9277         this.updateIndexes(0);
9278     },
9279
9280     /**
9281      * Function to override to reformat the data that is sent to
9282      * the template for each node.
9283      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9284      * a JSON object for an UpdateManager bound view).
9285      */
9286     prepareData : function(data){
9287         return data;
9288     },
9289
9290     onUpdate : function(ds, record){
9291         this.clearSelections();
9292         var index = this.store.indexOf(record);
9293         var n = this.nodes[index];
9294         this.tpl.insertBefore(n, this.prepareData(record.data));
9295         n.parentNode.removeChild(n);
9296         this.updateIndexes(index, index);
9297     },
9298
9299     onAdd : function(ds, records, index){
9300         this.clearSelections();
9301         if(this.nodes.length == 0){
9302             this.refresh();
9303             return;
9304         }
9305         var n = this.nodes[index];
9306         for(var i = 0, len = records.length; i < len; i++){
9307             var d = this.prepareData(records[i].data);
9308             if(n){
9309                 this.tpl.insertBefore(n, d);
9310             }else{
9311                 this.tpl.append(this.el, d);
9312             }
9313         }
9314         this.updateIndexes(index);
9315     },
9316
9317     onRemove : function(ds, record, index){
9318         this.clearSelections();
9319         this.el.dom.removeChild(this.nodes[index]);
9320         this.updateIndexes(index);
9321     },
9322
9323     /**
9324      * Refresh an individual node.
9325      * @param {Number} index
9326      */
9327     refreshNode : function(index){
9328         this.onUpdate(this.store, this.store.getAt(index));
9329     },
9330
9331     updateIndexes : function(startIndex, endIndex){
9332         var ns = this.nodes;
9333         startIndex = startIndex || 0;
9334         endIndex = endIndex || ns.length - 1;
9335         for(var i = startIndex; i <= endIndex; i++){
9336             ns[i].nodeIndex = i;
9337         }
9338     },
9339
9340     /**
9341      * Changes the data store this view uses and refresh the view.
9342      * @param {Store} store
9343      */
9344     setStore : function(store, initial){
9345         if(!initial && this.store){
9346             this.store.un("datachanged", this.refresh);
9347             this.store.un("add", this.onAdd);
9348             this.store.un("remove", this.onRemove);
9349             this.store.un("update", this.onUpdate);
9350             this.store.un("clear", this.refresh);
9351         }
9352         if(store){
9353           
9354             store.on("datachanged", this.refresh, this);
9355             store.on("add", this.onAdd, this);
9356             store.on("remove", this.onRemove, this);
9357             store.on("update", this.onUpdate, this);
9358             store.on("clear", this.refresh, this);
9359         }
9360         
9361         if(store){
9362             this.refresh();
9363         }
9364     },
9365
9366     /**
9367      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9368      * @param {HTMLElement} node
9369      * @return {HTMLElement} The template node
9370      */
9371     findItemFromChild : function(node){
9372         var el = this.el.dom;
9373         if(!node || node.parentNode == el){
9374                     return node;
9375             }
9376             var p = node.parentNode;
9377             while(p && p != el){
9378             if(p.parentNode == el){
9379                 return p;
9380             }
9381             p = p.parentNode;
9382         }
9383             return null;
9384     },
9385
9386     /** @ignore */
9387     onClick : function(e){
9388         var item = this.findItemFromChild(e.getTarget());
9389         if(item){
9390             var index = this.indexOf(item);
9391             if(this.onItemClick(item, index, e) !== false){
9392                 this.fireEvent("click", this, index, item, e);
9393             }
9394         }else{
9395             this.clearSelections();
9396         }
9397     },
9398
9399     /** @ignore */
9400     onContextMenu : function(e){
9401         var item = this.findItemFromChild(e.getTarget());
9402         if(item){
9403             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9404         }
9405     },
9406
9407     /** @ignore */
9408     onDblClick : function(e){
9409         var item = this.findItemFromChild(e.getTarget());
9410         if(item){
9411             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9412         }
9413     },
9414
9415     onItemClick : function(item, index, e){
9416         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9417             return false;
9418         }
9419         if(this.multiSelect || this.singleSelect){
9420             if(this.multiSelect && e.shiftKey && this.lastSelection){
9421                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9422             }else{
9423                 this.select(item, this.multiSelect && e.ctrlKey);
9424                 this.lastSelection = item;
9425             }
9426             e.preventDefault();
9427         }
9428         return true;
9429     },
9430
9431     /**
9432      * Get the number of selected nodes.
9433      * @return {Number}
9434      */
9435     getSelectionCount : function(){
9436         return this.selections.length;
9437     },
9438
9439     /**
9440      * Get the currently selected nodes.
9441      * @return {Array} An array of HTMLElements
9442      */
9443     getSelectedNodes : function(){
9444         return this.selections;
9445     },
9446
9447     /**
9448      * Get the indexes of the selected nodes.
9449      * @return {Array}
9450      */
9451     getSelectedIndexes : function(){
9452         var indexes = [], s = this.selections;
9453         for(var i = 0, len = s.length; i < len; i++){
9454             indexes.push(s[i].nodeIndex);
9455         }
9456         return indexes;
9457     },
9458
9459     /**
9460      * Clear all selections
9461      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9462      */
9463     clearSelections : function(suppressEvent){
9464         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9465             this.cmp.elements = this.selections;
9466             this.cmp.removeClass(this.selectedClass);
9467             this.selections = [];
9468             if(!suppressEvent){
9469                 this.fireEvent("selectionchange", this, this.selections);
9470             }
9471         }
9472     },
9473
9474     /**
9475      * Returns true if the passed node is selected
9476      * @param {HTMLElement/Number} node The node or node index
9477      * @return {Boolean}
9478      */
9479     isSelected : function(node){
9480         var s = this.selections;
9481         if(s.length < 1){
9482             return false;
9483         }
9484         node = this.getNode(node);
9485         return s.indexOf(node) !== -1;
9486     },
9487
9488     /**
9489      * Selects nodes.
9490      * @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
9491      * @param {Boolean} keepExisting (optional) true to keep existing selections
9492      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9493      */
9494     select : function(nodeInfo, keepExisting, suppressEvent){
9495         if(nodeInfo instanceof Array){
9496             if(!keepExisting){
9497                 this.clearSelections(true);
9498             }
9499             for(var i = 0, len = nodeInfo.length; i < len; i++){
9500                 this.select(nodeInfo[i], true, true);
9501             }
9502         } else{
9503             var node = this.getNode(nodeInfo);
9504             if(node && !this.isSelected(node)){
9505                 if(!keepExisting){
9506                     this.clearSelections(true);
9507                 }
9508                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9509                     Roo.fly(node).addClass(this.selectedClass);
9510                     this.selections.push(node);
9511                     if(!suppressEvent){
9512                         this.fireEvent("selectionchange", this, this.selections);
9513                     }
9514                 }
9515             }
9516         }
9517     },
9518
9519     /**
9520      * Gets a template node.
9521      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9522      * @return {HTMLElement} The node or null if it wasn't found
9523      */
9524     getNode : function(nodeInfo){
9525         if(typeof nodeInfo == "string"){
9526             return document.getElementById(nodeInfo);
9527         }else if(typeof nodeInfo == "number"){
9528             return this.nodes[nodeInfo];
9529         }
9530         return nodeInfo;
9531     },
9532
9533     /**
9534      * Gets a range template nodes.
9535      * @param {Number} startIndex
9536      * @param {Number} endIndex
9537      * @return {Array} An array of nodes
9538      */
9539     getNodes : function(start, end){
9540         var ns = this.nodes;
9541         start = start || 0;
9542         end = typeof end == "undefined" ? ns.length - 1 : end;
9543         var nodes = [];
9544         if(start <= end){
9545             for(var i = start; i <= end; i++){
9546                 nodes.push(ns[i]);
9547             }
9548         } else{
9549             for(var i = start; i >= end; i--){
9550                 nodes.push(ns[i]);
9551             }
9552         }
9553         return nodes;
9554     },
9555
9556     /**
9557      * Finds the index of the passed node
9558      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9559      * @return {Number} The index of the node or -1
9560      */
9561     indexOf : function(node){
9562         node = this.getNode(node);
9563         if(typeof node.nodeIndex == "number"){
9564             return node.nodeIndex;
9565         }
9566         var ns = this.nodes;
9567         for(var i = 0, len = ns.length; i < len; i++){
9568             if(ns[i] == node){
9569                 return i;
9570             }
9571         }
9572         return -1;
9573     }
9574 });
9575 /*
9576  * Based on:
9577  * Ext JS Library 1.1.1
9578  * Copyright(c) 2006-2007, Ext JS, LLC.
9579  *
9580  * Originally Released Under LGPL - original licence link has changed is not relivant.
9581  *
9582  * Fork - LGPL
9583  * <script type="text/javascript">
9584  */
9585
9586 /**
9587  * @class Roo.JsonView
9588  * @extends Roo.View
9589  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9590 <pre><code>
9591 var view = new Roo.JsonView({
9592     container: "my-element",
9593     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9594     multiSelect: true, 
9595     jsonRoot: "data" 
9596 });
9597
9598 // listen for node click?
9599 view.on("click", function(vw, index, node, e){
9600     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9601 });
9602
9603 // direct load of JSON data
9604 view.load("foobar.php");
9605
9606 // Example from my blog list
9607 var tpl = new Roo.Template(
9608     '&lt;div class="entry"&gt;' +
9609     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9610     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9611     "&lt;/div&gt;&lt;hr /&gt;"
9612 );
9613
9614 var moreView = new Roo.JsonView({
9615     container :  "entry-list", 
9616     template : tpl,
9617     jsonRoot: "posts"
9618 });
9619 moreView.on("beforerender", this.sortEntries, this);
9620 moreView.load({
9621     url: "/blog/get-posts.php",
9622     params: "allposts=true",
9623     text: "Loading Blog Entries..."
9624 });
9625 </code></pre>
9626
9627 * Note: old code is supported with arguments : (container, template, config)
9628
9629
9630  * @constructor
9631  * Create a new JsonView
9632  * 
9633  * @param {Object} config The config object
9634  * 
9635  */
9636 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9637     
9638     
9639     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9640
9641     var um = this.el.getUpdateManager();
9642     um.setRenderer(this);
9643     um.on("update", this.onLoad, this);
9644     um.on("failure", this.onLoadException, this);
9645
9646     /**
9647      * @event beforerender
9648      * Fires before rendering of the downloaded JSON data.
9649      * @param {Roo.JsonView} this
9650      * @param {Object} data The JSON data loaded
9651      */
9652     /**
9653      * @event load
9654      * Fires when data is loaded.
9655      * @param {Roo.JsonView} this
9656      * @param {Object} data The JSON data loaded
9657      * @param {Object} response The raw Connect response object
9658      */
9659     /**
9660      * @event loadexception
9661      * Fires when loading fails.
9662      * @param {Roo.JsonView} this
9663      * @param {Object} response The raw Connect response object
9664      */
9665     this.addEvents({
9666         'beforerender' : true,
9667         'load' : true,
9668         'loadexception' : true
9669     });
9670 };
9671 Roo.extend(Roo.JsonView, Roo.View, {
9672     /**
9673      * @type {String} The root property in the loaded JSON object that contains the data
9674      */
9675     jsonRoot : "",
9676
9677     /**
9678      * Refreshes the view.
9679      */
9680     refresh : function(){
9681         this.clearSelections();
9682         this.el.update("");
9683         var html = [];
9684         var o = this.jsonData;
9685         if(o && o.length > 0){
9686             for(var i = 0, len = o.length; i < len; i++){
9687                 var data = this.prepareData(o[i], i, o);
9688                 html[html.length] = this.tpl.apply(data);
9689             }
9690         }else{
9691             html.push(this.emptyText);
9692         }
9693         this.el.update(html.join(""));
9694         this.nodes = this.el.dom.childNodes;
9695         this.updateIndexes(0);
9696     },
9697
9698     /**
9699      * 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.
9700      * @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:
9701      <pre><code>
9702      view.load({
9703          url: "your-url.php",
9704          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9705          callback: yourFunction,
9706          scope: yourObject, //(optional scope)
9707          discardUrl: false,
9708          nocache: false,
9709          text: "Loading...",
9710          timeout: 30,
9711          scripts: false
9712      });
9713      </code></pre>
9714      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9715      * 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.
9716      * @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}
9717      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9718      * @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.
9719      */
9720     load : function(){
9721         var um = this.el.getUpdateManager();
9722         um.update.apply(um, arguments);
9723     },
9724
9725     render : function(el, response){
9726         this.clearSelections();
9727         this.el.update("");
9728         var o;
9729         try{
9730             o = Roo.util.JSON.decode(response.responseText);
9731             if(this.jsonRoot){
9732                 
9733                 o = o[this.jsonRoot];
9734             }
9735         } catch(e){
9736         }
9737         /**
9738          * The current JSON data or null
9739          */
9740         this.jsonData = o;
9741         this.beforeRender();
9742         this.refresh();
9743     },
9744
9745 /**
9746  * Get the number of records in the current JSON dataset
9747  * @return {Number}
9748  */
9749     getCount : function(){
9750         return this.jsonData ? this.jsonData.length : 0;
9751     },
9752
9753 /**
9754  * Returns the JSON object for the specified node(s)
9755  * @param {HTMLElement/Array} node The node or an array of nodes
9756  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9757  * you get the JSON object for the node
9758  */
9759     getNodeData : function(node){
9760         if(node instanceof Array){
9761             var data = [];
9762             for(var i = 0, len = node.length; i < len; i++){
9763                 data.push(this.getNodeData(node[i]));
9764             }
9765             return data;
9766         }
9767         return this.jsonData[this.indexOf(node)] || null;
9768     },
9769
9770     beforeRender : function(){
9771         this.snapshot = this.jsonData;
9772         if(this.sortInfo){
9773             this.sort.apply(this, this.sortInfo);
9774         }
9775         this.fireEvent("beforerender", this, this.jsonData);
9776     },
9777
9778     onLoad : function(el, o){
9779         this.fireEvent("load", this, this.jsonData, o);
9780     },
9781
9782     onLoadException : function(el, o){
9783         this.fireEvent("loadexception", this, o);
9784     },
9785
9786 /**
9787  * Filter the data by a specific property.
9788  * @param {String} property A property on your JSON objects
9789  * @param {String/RegExp} value Either string that the property values
9790  * should start with, or a RegExp to test against the property
9791  */
9792     filter : function(property, value){
9793         if(this.jsonData){
9794             var data = [];
9795             var ss = this.snapshot;
9796             if(typeof value == "string"){
9797                 var vlen = value.length;
9798                 if(vlen == 0){
9799                     this.clearFilter();
9800                     return;
9801                 }
9802                 value = value.toLowerCase();
9803                 for(var i = 0, len = ss.length; i < len; i++){
9804                     var o = ss[i];
9805                     if(o[property].substr(0, vlen).toLowerCase() == value){
9806                         data.push(o);
9807                     }
9808                 }
9809             } else if(value.exec){ // regex?
9810                 for(var i = 0, len = ss.length; i < len; i++){
9811                     var o = ss[i];
9812                     if(value.test(o[property])){
9813                         data.push(o);
9814                     }
9815                 }
9816             } else{
9817                 return;
9818             }
9819             this.jsonData = data;
9820             this.refresh();
9821         }
9822     },
9823
9824 /**
9825  * Filter by a function. The passed function will be called with each
9826  * object in the current dataset. If the function returns true the value is kept,
9827  * otherwise it is filtered.
9828  * @param {Function} fn
9829  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9830  */
9831     filterBy : function(fn, scope){
9832         if(this.jsonData){
9833             var data = [];
9834             var ss = this.snapshot;
9835             for(var i = 0, len = ss.length; i < len; i++){
9836                 var o = ss[i];
9837                 if(fn.call(scope || this, o)){
9838                     data.push(o);
9839                 }
9840             }
9841             this.jsonData = data;
9842             this.refresh();
9843         }
9844     },
9845
9846 /**
9847  * Clears the current filter.
9848  */
9849     clearFilter : function(){
9850         if(this.snapshot && this.jsonData != this.snapshot){
9851             this.jsonData = this.snapshot;
9852             this.refresh();
9853         }
9854     },
9855
9856
9857 /**
9858  * Sorts the data for this view and refreshes it.
9859  * @param {String} property A property on your JSON objects to sort on
9860  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9861  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9862  */
9863     sort : function(property, dir, sortType){
9864         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9865         if(this.jsonData){
9866             var p = property;
9867             var dsc = dir && dir.toLowerCase() == "desc";
9868             var f = function(o1, o2){
9869                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9870                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9871                 ;
9872                 if(v1 < v2){
9873                     return dsc ? +1 : -1;
9874                 } else if(v1 > v2){
9875                     return dsc ? -1 : +1;
9876                 } else{
9877                     return 0;
9878                 }
9879             };
9880             this.jsonData.sort(f);
9881             this.refresh();
9882             if(this.jsonData != this.snapshot){
9883                 this.snapshot.sort(f);
9884             }
9885         }
9886     }
9887 });/*
9888  * Based on:
9889  * Ext JS Library 1.1.1
9890  * Copyright(c) 2006-2007, Ext JS, LLC.
9891  *
9892  * Originally Released Under LGPL - original licence link has changed is not relivant.
9893  *
9894  * Fork - LGPL
9895  * <script type="text/javascript">
9896  */
9897  
9898
9899 /**
9900  * @class Roo.ColorPalette
9901  * @extends Roo.Component
9902  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
9903  * Here's an example of typical usage:
9904  * <pre><code>
9905 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
9906 cp.render('my-div');
9907
9908 cp.on('select', function(palette, selColor){
9909     // do something with selColor
9910 });
9911 </code></pre>
9912  * @constructor
9913  * Create a new ColorPalette
9914  * @param {Object} config The config object
9915  */
9916 Roo.ColorPalette = function(config){
9917     Roo.ColorPalette.superclass.constructor.call(this, config);
9918     this.addEvents({
9919         /**
9920              * @event select
9921              * Fires when a color is selected
9922              * @param {ColorPalette} this
9923              * @param {String} color The 6-digit color hex code (without the # symbol)
9924              */
9925         select: true
9926     });
9927
9928     if(this.handler){
9929         this.on("select", this.handler, this.scope, true);
9930     }
9931 };
9932 Roo.extend(Roo.ColorPalette, Roo.Component, {
9933     /**
9934      * @cfg {String} itemCls
9935      * The CSS class to apply to the containing element (defaults to "x-color-palette")
9936      */
9937     itemCls : "x-color-palette",
9938     /**
9939      * @cfg {String} value
9940      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
9941      * the hex codes are case-sensitive.
9942      */
9943     value : null,
9944     clickEvent:'click',
9945     // private
9946     ctype: "Roo.ColorPalette",
9947
9948     /**
9949      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
9950      */
9951     allowReselect : false,
9952
9953     /**
9954      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
9955      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
9956      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
9957      * of colors with the width setting until the box is symmetrical.</p>
9958      * <p>You can override individual colors if needed:</p>
9959      * <pre><code>
9960 var cp = new Roo.ColorPalette();
9961 cp.colors[0] = "FF0000";  // change the first box to red
9962 </code></pre>
9963
9964 Or you can provide a custom array of your own for complete control:
9965 <pre><code>
9966 var cp = new Roo.ColorPalette();
9967 cp.colors = ["000000", "993300", "333300"];
9968 </code></pre>
9969      * @type Array
9970      */
9971     colors : [
9972         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
9973         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
9974         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
9975         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
9976         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
9977     ],
9978
9979     // private
9980     onRender : function(container, position){
9981         var t = new Roo.MasterTemplate(
9982             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
9983         );
9984         var c = this.colors;
9985         for(var i = 0, len = c.length; i < len; i++){
9986             t.add([c[i]]);
9987         }
9988         var el = document.createElement("div");
9989         el.className = this.itemCls;
9990         t.overwrite(el);
9991         container.dom.insertBefore(el, position);
9992         this.el = Roo.get(el);
9993         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
9994         if(this.clickEvent != 'click'){
9995             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
9996         }
9997     },
9998
9999     // private
10000     afterRender : function(){
10001         Roo.ColorPalette.superclass.afterRender.call(this);
10002         if(this.value){
10003             var s = this.value;
10004             this.value = null;
10005             this.select(s);
10006         }
10007     },
10008
10009     // private
10010     handleClick : function(e, t){
10011         e.preventDefault();
10012         if(!this.disabled){
10013             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
10014             this.select(c.toUpperCase());
10015         }
10016     },
10017
10018     /**
10019      * Selects the specified color in the palette (fires the select event)
10020      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
10021      */
10022     select : function(color){
10023         color = color.replace("#", "");
10024         if(color != this.value || this.allowReselect){
10025             var el = this.el;
10026             if(this.value){
10027                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
10028             }
10029             el.child("a.color-"+color).addClass("x-color-palette-sel");
10030             this.value = color;
10031             this.fireEvent("select", this, color);
10032         }
10033     }
10034 });/*
10035  * Based on:
10036  * Ext JS Library 1.1.1
10037  * Copyright(c) 2006-2007, Ext JS, LLC.
10038  *
10039  * Originally Released Under LGPL - original licence link has changed is not relivant.
10040  *
10041  * Fork - LGPL
10042  * <script type="text/javascript">
10043  */
10044  
10045 /**
10046  * @class Roo.DatePicker
10047  * @extends Roo.Component
10048  * Simple date picker class.
10049  * @constructor
10050  * Create a new DatePicker
10051  * @param {Object} config The config object
10052  */
10053 Roo.DatePicker = function(config){
10054     Roo.DatePicker.superclass.constructor.call(this, config);
10055
10056     this.value = config && config.value ?
10057                  config.value.clearTime() : new Date().clearTime();
10058
10059     this.addEvents({
10060         /**
10061              * @event select
10062              * Fires when a date is selected
10063              * @param {DatePicker} this
10064              * @param {Date} date The selected date
10065              */
10066         select: true
10067     });
10068
10069     if(this.handler){
10070         this.on("select", this.handler,  this.scope || this);
10071     }
10072     // build the disabledDatesRE
10073     if(!this.disabledDatesRE && this.disabledDates){
10074         var dd = this.disabledDates;
10075         var re = "(?:";
10076         for(var i = 0; i < dd.length; i++){
10077             re += dd[i];
10078             if(i != dd.length-1) re += "|";
10079         }
10080         this.disabledDatesRE = new RegExp(re + ")");
10081     }
10082 };
10083
10084 Roo.extend(Roo.DatePicker, Roo.Component, {
10085     /**
10086      * @cfg {String} todayText
10087      * The text to display on the button that selects the current date (defaults to "Today")
10088      */
10089     todayText : "Today",
10090     /**
10091      * @cfg {String} okText
10092      * The text to display on the ok button
10093      */
10094     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10095     /**
10096      * @cfg {String} cancelText
10097      * The text to display on the cancel button
10098      */
10099     cancelText : "Cancel",
10100     /**
10101      * @cfg {String} todayTip
10102      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10103      */
10104     todayTip : "{0} (Spacebar)",
10105     /**
10106      * @cfg {Date} minDate
10107      * Minimum allowable date (JavaScript date object, defaults to null)
10108      */
10109     minDate : null,
10110     /**
10111      * @cfg {Date} maxDate
10112      * Maximum allowable date (JavaScript date object, defaults to null)
10113      */
10114     maxDate : null,
10115     /**
10116      * @cfg {String} minText
10117      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10118      */
10119     minText : "This date is before the minimum date",
10120     /**
10121      * @cfg {String} maxText
10122      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10123      */
10124     maxText : "This date is after the maximum date",
10125     /**
10126      * @cfg {String} format
10127      * The default date format string which can be overriden for localization support.  The format must be
10128      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10129      */
10130     format : "m/d/y",
10131     /**
10132      * @cfg {Array} disabledDays
10133      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10134      */
10135     disabledDays : null,
10136     /**
10137      * @cfg {String} disabledDaysText
10138      * The tooltip to display when the date falls on a disabled day (defaults to "")
10139      */
10140     disabledDaysText : "",
10141     /**
10142      * @cfg {RegExp} disabledDatesRE
10143      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10144      */
10145     disabledDatesRE : null,
10146     /**
10147      * @cfg {String} disabledDatesText
10148      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10149      */
10150     disabledDatesText : "",
10151     /**
10152      * @cfg {Boolean} constrainToViewport
10153      * True to constrain the date picker to the viewport (defaults to true)
10154      */
10155     constrainToViewport : true,
10156     /**
10157      * @cfg {Array} monthNames
10158      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10159      */
10160     monthNames : Date.monthNames,
10161     /**
10162      * @cfg {Array} dayNames
10163      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10164      */
10165     dayNames : Date.dayNames,
10166     /**
10167      * @cfg {String} nextText
10168      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10169      */
10170     nextText: 'Next Month (Control+Right)',
10171     /**
10172      * @cfg {String} prevText
10173      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10174      */
10175     prevText: 'Previous Month (Control+Left)',
10176     /**
10177      * @cfg {String} monthYearText
10178      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10179      */
10180     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10181     /**
10182      * @cfg {Number} startDay
10183      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10184      */
10185     startDay : 0,
10186     /**
10187      * @cfg {Bool} showClear
10188      * Show a clear button (usefull for date form elements that can be blank.)
10189      */
10190     
10191     showClear: false,
10192     
10193     /**
10194      * Sets the value of the date field
10195      * @param {Date} value The date to set
10196      */
10197     setValue : function(value){
10198         var old = this.value;
10199         this.value = value.clearTime(true);
10200         if(this.el){
10201             this.update(this.value);
10202         }
10203     },
10204
10205     /**
10206      * Gets the current selected value of the date field
10207      * @return {Date} The selected date
10208      */
10209     getValue : function(){
10210         return this.value;
10211     },
10212
10213     // private
10214     focus : function(){
10215         if(this.el){
10216             this.update(this.activeDate);
10217         }
10218     },
10219
10220     // private
10221     onRender : function(container, position){
10222         var m = [
10223              '<table cellspacing="0">',
10224                 '<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>',
10225                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10226         var dn = this.dayNames;
10227         for(var i = 0; i < 7; i++){
10228             var d = this.startDay+i;
10229             if(d > 6){
10230                 d = d-7;
10231             }
10232             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10233         }
10234         m[m.length] = "</tr></thead><tbody><tr>";
10235         for(var i = 0; i < 42; i++) {
10236             if(i % 7 == 0 && i != 0){
10237                 m[m.length] = "</tr><tr>";
10238             }
10239             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10240         }
10241         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10242             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10243
10244         var el = document.createElement("div");
10245         el.className = "x-date-picker";
10246         el.innerHTML = m.join("");
10247
10248         container.dom.insertBefore(el, position);
10249
10250         this.el = Roo.get(el);
10251         this.eventEl = Roo.get(el.firstChild);
10252
10253         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10254             handler: this.showPrevMonth,
10255             scope: this,
10256             preventDefault:true,
10257             stopDefault:true
10258         });
10259
10260         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10261             handler: this.showNextMonth,
10262             scope: this,
10263             preventDefault:true,
10264             stopDefault:true
10265         });
10266
10267         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10268
10269         this.monthPicker = this.el.down('div.x-date-mp');
10270         this.monthPicker.enableDisplayMode('block');
10271         
10272         var kn = new Roo.KeyNav(this.eventEl, {
10273             "left" : function(e){
10274                 e.ctrlKey ?
10275                     this.showPrevMonth() :
10276                     this.update(this.activeDate.add("d", -1));
10277             },
10278
10279             "right" : function(e){
10280                 e.ctrlKey ?
10281                     this.showNextMonth() :
10282                     this.update(this.activeDate.add("d", 1));
10283             },
10284
10285             "up" : function(e){
10286                 e.ctrlKey ?
10287                     this.showNextYear() :
10288                     this.update(this.activeDate.add("d", -7));
10289             },
10290
10291             "down" : function(e){
10292                 e.ctrlKey ?
10293                     this.showPrevYear() :
10294                     this.update(this.activeDate.add("d", 7));
10295             },
10296
10297             "pageUp" : function(e){
10298                 this.showNextMonth();
10299             },
10300
10301             "pageDown" : function(e){
10302                 this.showPrevMonth();
10303             },
10304
10305             "enter" : function(e){
10306                 e.stopPropagation();
10307                 return true;
10308             },
10309
10310             scope : this
10311         });
10312
10313         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10314
10315         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10316
10317         this.el.unselectable();
10318         
10319         this.cells = this.el.select("table.x-date-inner tbody td");
10320         this.textNodes = this.el.query("table.x-date-inner tbody span");
10321
10322         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10323             text: "&#160;",
10324             tooltip: this.monthYearText
10325         });
10326
10327         this.mbtn.on('click', this.showMonthPicker, this);
10328         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10329
10330
10331         var today = (new Date()).dateFormat(this.format);
10332         
10333         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10334         if (this.showClear) {
10335             baseTb.add( new Roo.Toolbar.Fill());
10336         }
10337         baseTb.add({
10338             text: String.format(this.todayText, today),
10339             tooltip: String.format(this.todayTip, today),
10340             handler: this.selectToday,
10341             scope: this
10342         });
10343         
10344         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10345             
10346         //});
10347         if (this.showClear) {
10348             
10349             baseTb.add( new Roo.Toolbar.Fill());
10350             baseTb.add({
10351                 text: '&#160;',
10352                 cls: 'x-btn-icon x-btn-clear',
10353                 handler: function() {
10354                     //this.value = '';
10355                     this.fireEvent("select", this, '');
10356                 },
10357                 scope: this
10358             });
10359         }
10360         
10361         
10362         if(Roo.isIE){
10363             this.el.repaint();
10364         }
10365         this.update(this.value);
10366     },
10367
10368     createMonthPicker : function(){
10369         if(!this.monthPicker.dom.firstChild){
10370             var buf = ['<table border="0" cellspacing="0">'];
10371             for(var i = 0; i < 6; i++){
10372                 buf.push(
10373                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10374                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10375                     i == 0 ?
10376                     '<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>' :
10377                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10378                 );
10379             }
10380             buf.push(
10381                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10382                     this.okText,
10383                     '</button><button type="button" class="x-date-mp-cancel">',
10384                     this.cancelText,
10385                     '</button></td></tr>',
10386                 '</table>'
10387             );
10388             this.monthPicker.update(buf.join(''));
10389             this.monthPicker.on('click', this.onMonthClick, this);
10390             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10391
10392             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10393             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10394
10395             this.mpMonths.each(function(m, a, i){
10396                 i += 1;
10397                 if((i%2) == 0){
10398                     m.dom.xmonth = 5 + Math.round(i * .5);
10399                 }else{
10400                     m.dom.xmonth = Math.round((i-1) * .5);
10401                 }
10402             });
10403         }
10404     },
10405
10406     showMonthPicker : function(){
10407         this.createMonthPicker();
10408         var size = this.el.getSize();
10409         this.monthPicker.setSize(size);
10410         this.monthPicker.child('table').setSize(size);
10411
10412         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10413         this.updateMPMonth(this.mpSelMonth);
10414         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10415         this.updateMPYear(this.mpSelYear);
10416
10417         this.monthPicker.slideIn('t', {duration:.2});
10418     },
10419
10420     updateMPYear : function(y){
10421         this.mpyear = y;
10422         var ys = this.mpYears.elements;
10423         for(var i = 1; i <= 10; i++){
10424             var td = ys[i-1], y2;
10425             if((i%2) == 0){
10426                 y2 = y + Math.round(i * .5);
10427                 td.firstChild.innerHTML = y2;
10428                 td.xyear = y2;
10429             }else{
10430                 y2 = y - (5-Math.round(i * .5));
10431                 td.firstChild.innerHTML = y2;
10432                 td.xyear = y2;
10433             }
10434             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10435         }
10436     },
10437
10438     updateMPMonth : function(sm){
10439         this.mpMonths.each(function(m, a, i){
10440             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10441         });
10442     },
10443
10444     selectMPMonth: function(m){
10445         
10446     },
10447
10448     onMonthClick : function(e, t){
10449         e.stopEvent();
10450         var el = new Roo.Element(t), pn;
10451         if(el.is('button.x-date-mp-cancel')){
10452             this.hideMonthPicker();
10453         }
10454         else if(el.is('button.x-date-mp-ok')){
10455             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10456             this.hideMonthPicker();
10457         }
10458         else if(pn = el.up('td.x-date-mp-month', 2)){
10459             this.mpMonths.removeClass('x-date-mp-sel');
10460             pn.addClass('x-date-mp-sel');
10461             this.mpSelMonth = pn.dom.xmonth;
10462         }
10463         else if(pn = el.up('td.x-date-mp-year', 2)){
10464             this.mpYears.removeClass('x-date-mp-sel');
10465             pn.addClass('x-date-mp-sel');
10466             this.mpSelYear = pn.dom.xyear;
10467         }
10468         else if(el.is('a.x-date-mp-prev')){
10469             this.updateMPYear(this.mpyear-10);
10470         }
10471         else if(el.is('a.x-date-mp-next')){
10472             this.updateMPYear(this.mpyear+10);
10473         }
10474     },
10475
10476     onMonthDblClick : function(e, t){
10477         e.stopEvent();
10478         var el = new Roo.Element(t), pn;
10479         if(pn = el.up('td.x-date-mp-month', 2)){
10480             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10481             this.hideMonthPicker();
10482         }
10483         else if(pn = el.up('td.x-date-mp-year', 2)){
10484             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10485             this.hideMonthPicker();
10486         }
10487     },
10488
10489     hideMonthPicker : function(disableAnim){
10490         if(this.monthPicker){
10491             if(disableAnim === true){
10492                 this.monthPicker.hide();
10493             }else{
10494                 this.monthPicker.slideOut('t', {duration:.2});
10495             }
10496         }
10497     },
10498
10499     // private
10500     showPrevMonth : function(e){
10501         this.update(this.activeDate.add("mo", -1));
10502     },
10503
10504     // private
10505     showNextMonth : function(e){
10506         this.update(this.activeDate.add("mo", 1));
10507     },
10508
10509     // private
10510     showPrevYear : function(){
10511         this.update(this.activeDate.add("y", -1));
10512     },
10513
10514     // private
10515     showNextYear : function(){
10516         this.update(this.activeDate.add("y", 1));
10517     },
10518
10519     // private
10520     handleMouseWheel : function(e){
10521         var delta = e.getWheelDelta();
10522         if(delta > 0){
10523             this.showPrevMonth();
10524             e.stopEvent();
10525         } else if(delta < 0){
10526             this.showNextMonth();
10527             e.stopEvent();
10528         }
10529     },
10530
10531     // private
10532     handleDateClick : function(e, t){
10533         e.stopEvent();
10534         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10535             this.setValue(new Date(t.dateValue));
10536             this.fireEvent("select", this, this.value);
10537         }
10538     },
10539
10540     // private
10541     selectToday : function(){
10542         this.setValue(new Date().clearTime());
10543         this.fireEvent("select", this, this.value);
10544     },
10545
10546     // private
10547     update : function(date){
10548         var vd = this.activeDate;
10549         this.activeDate = date;
10550         if(vd && this.el){
10551             var t = date.getTime();
10552             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10553                 this.cells.removeClass("x-date-selected");
10554                 this.cells.each(function(c){
10555                    if(c.dom.firstChild.dateValue == t){
10556                        c.addClass("x-date-selected");
10557                        setTimeout(function(){
10558                             try{c.dom.firstChild.focus();}catch(e){}
10559                        }, 50);
10560                        return false;
10561                    }
10562                 });
10563                 return;
10564             }
10565         }
10566         var days = date.getDaysInMonth();
10567         var firstOfMonth = date.getFirstDateOfMonth();
10568         var startingPos = firstOfMonth.getDay()-this.startDay;
10569
10570         if(startingPos <= this.startDay){
10571             startingPos += 7;
10572         }
10573
10574         var pm = date.add("mo", -1);
10575         var prevStart = pm.getDaysInMonth()-startingPos;
10576
10577         var cells = this.cells.elements;
10578         var textEls = this.textNodes;
10579         days += startingPos;
10580
10581         // convert everything to numbers so it's fast
10582         var day = 86400000;
10583         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10584         var today = new Date().clearTime().getTime();
10585         var sel = date.clearTime().getTime();
10586         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10587         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10588         var ddMatch = this.disabledDatesRE;
10589         var ddText = this.disabledDatesText;
10590         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10591         var ddaysText = this.disabledDaysText;
10592         var format = this.format;
10593
10594         var setCellClass = function(cal, cell){
10595             cell.title = "";
10596             var t = d.getTime();
10597             cell.firstChild.dateValue = t;
10598             if(t == today){
10599                 cell.className += " x-date-today";
10600                 cell.title = cal.todayText;
10601             }
10602             if(t == sel){
10603                 cell.className += " x-date-selected";
10604                 setTimeout(function(){
10605                     try{cell.firstChild.focus();}catch(e){}
10606                 }, 50);
10607             }
10608             // disabling
10609             if(t < min) {
10610                 cell.className = " x-date-disabled";
10611                 cell.title = cal.minText;
10612                 return;
10613             }
10614             if(t > max) {
10615                 cell.className = " x-date-disabled";
10616                 cell.title = cal.maxText;
10617                 return;
10618             }
10619             if(ddays){
10620                 if(ddays.indexOf(d.getDay()) != -1){
10621                     cell.title = ddaysText;
10622                     cell.className = " x-date-disabled";
10623                 }
10624             }
10625             if(ddMatch && format){
10626                 var fvalue = d.dateFormat(format);
10627                 if(ddMatch.test(fvalue)){
10628                     cell.title = ddText.replace("%0", fvalue);
10629                     cell.className = " x-date-disabled";
10630                 }
10631             }
10632         };
10633
10634         var i = 0;
10635         for(; i < startingPos; i++) {
10636             textEls[i].innerHTML = (++prevStart);
10637             d.setDate(d.getDate()+1);
10638             cells[i].className = "x-date-prevday";
10639             setCellClass(this, cells[i]);
10640         }
10641         for(; i < days; i++){
10642             intDay = i - startingPos + 1;
10643             textEls[i].innerHTML = (intDay);
10644             d.setDate(d.getDate()+1);
10645             cells[i].className = "x-date-active";
10646             setCellClass(this, cells[i]);
10647         }
10648         var extraDays = 0;
10649         for(; i < 42; i++) {
10650              textEls[i].innerHTML = (++extraDays);
10651              d.setDate(d.getDate()+1);
10652              cells[i].className = "x-date-nextday";
10653              setCellClass(this, cells[i]);
10654         }
10655
10656         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10657
10658         if(!this.internalRender){
10659             var main = this.el.dom.firstChild;
10660             var w = main.offsetWidth;
10661             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10662             Roo.fly(main).setWidth(w);
10663             this.internalRender = true;
10664             // opera does not respect the auto grow header center column
10665             // then, after it gets a width opera refuses to recalculate
10666             // without a second pass
10667             if(Roo.isOpera && !this.secondPass){
10668                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10669                 this.secondPass = true;
10670                 this.update.defer(10, this, [date]);
10671             }
10672         }
10673     }
10674 });/*
10675  * Based on:
10676  * Ext JS Library 1.1.1
10677  * Copyright(c) 2006-2007, Ext JS, LLC.
10678  *
10679  * Originally Released Under LGPL - original licence link has changed is not relivant.
10680  *
10681  * Fork - LGPL
10682  * <script type="text/javascript">
10683  */
10684 /**
10685  * @class Roo.TabPanel
10686  * @extends Roo.util.Observable
10687  * A lightweight tab container.
10688  * <br><br>
10689  * Usage:
10690  * <pre><code>
10691 // basic tabs 1, built from existing content
10692 var tabs = new Roo.TabPanel("tabs1");
10693 tabs.addTab("script", "View Script");
10694 tabs.addTab("markup", "View Markup");
10695 tabs.activate("script");
10696
10697 // more advanced tabs, built from javascript
10698 var jtabs = new Roo.TabPanel("jtabs");
10699 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10700
10701 // set up the UpdateManager
10702 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10703 var updater = tab2.getUpdateManager();
10704 updater.setDefaultUrl("ajax1.htm");
10705 tab2.on('activate', updater.refresh, updater, true);
10706
10707 // Use setUrl for Ajax loading
10708 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10709 tab3.setUrl("ajax2.htm", null, true);
10710
10711 // Disabled tab
10712 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10713 tab4.disable();
10714
10715 jtabs.activate("jtabs-1");
10716  * </code></pre>
10717  * @constructor
10718  * Create a new TabPanel.
10719  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10720  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10721  */
10722 Roo.TabPanel = function(container, config){
10723     /**
10724     * The container element for this TabPanel.
10725     * @type Roo.Element
10726     */
10727     this.el = Roo.get(container, true);
10728     if(config){
10729         if(typeof config == "boolean"){
10730             this.tabPosition = config ? "bottom" : "top";
10731         }else{
10732             Roo.apply(this, config);
10733         }
10734     }
10735     if(this.tabPosition == "bottom"){
10736         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10737         this.el.addClass("x-tabs-bottom");
10738     }
10739     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10740     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10741     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10742     if(Roo.isIE){
10743         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10744     }
10745     if(this.tabPosition != "bottom"){
10746     /** The body element that contains {@link Roo.TabPanelItem} bodies.
10747      * @type Roo.Element
10748      */
10749       this.bodyEl = Roo.get(this.createBody(this.el.dom));
10750       this.el.addClass("x-tabs-top");
10751     }
10752     this.items = [];
10753
10754     this.bodyEl.setStyle("position", "relative");
10755
10756     this.active = null;
10757     this.activateDelegate = this.activate.createDelegate(this);
10758
10759     this.addEvents({
10760         /**
10761          * @event tabchange
10762          * Fires when the active tab changes
10763          * @param {Roo.TabPanel} this
10764          * @param {Roo.TabPanelItem} activePanel The new active tab
10765          */
10766         "tabchange": true,
10767         /**
10768          * @event beforetabchange
10769          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10770          * @param {Roo.TabPanel} this
10771          * @param {Object} e Set cancel to true on this object to cancel the tab change
10772          * @param {Roo.TabPanelItem} tab The tab being changed to
10773          */
10774         "beforetabchange" : true
10775     });
10776
10777     Roo.EventManager.onWindowResize(this.onResize, this);
10778     this.cpad = this.el.getPadding("lr");
10779     this.hiddenCount = 0;
10780
10781     Roo.TabPanel.superclass.constructor.call(this);
10782 };
10783
10784 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10785         /*
10786          *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10787          */
10788     tabPosition : "top",
10789         /*
10790          *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10791          */
10792     currentTabWidth : 0,
10793         /*
10794          *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10795          */
10796     minTabWidth : 40,
10797         /*
10798          *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10799          */
10800     maxTabWidth : 250,
10801         /*
10802          *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10803          */
10804     preferredTabWidth : 175,
10805         /*
10806          *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10807          */
10808     resizeTabs : false,
10809         /*
10810          *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10811          */
10812     monitorResize : true,
10813
10814     /**
10815      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10816      * @param {String} id The id of the div to use <b>or create</b>
10817      * @param {String} text The text for the tab
10818      * @param {String} content (optional) Content to put in the TabPanelItem body
10819      * @param {Boolean} closable (optional) True to create a close icon on the tab
10820      * @return {Roo.TabPanelItem} The created TabPanelItem
10821      */
10822     addTab : function(id, text, content, closable){
10823         var item = new Roo.TabPanelItem(this, id, text, closable);
10824         this.addTabItem(item);
10825         if(content){
10826             item.setContent(content);
10827         }
10828         return item;
10829     },
10830
10831     /**
10832      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10833      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10834      * @return {Roo.TabPanelItem}
10835      */
10836     getTab : function(id){
10837         return this.items[id];
10838     },
10839
10840     /**
10841      * Hides the {@link Roo.TabPanelItem} with the specified id/index
10842      * @param {String/Number} id The id or index of the TabPanelItem to hide.
10843      */
10844     hideTab : function(id){
10845         var t = this.items[id];
10846         if(!t.isHidden()){
10847            t.setHidden(true);
10848            this.hiddenCount++;
10849            this.autoSizeTabs();
10850         }
10851     },
10852
10853     /**
10854      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10855      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10856      */
10857     unhideTab : function(id){
10858         var t = this.items[id];
10859         if(t.isHidden()){
10860            t.setHidden(false);
10861            this.hiddenCount--;
10862            this.autoSizeTabs();
10863         }
10864     },
10865
10866     /**
10867      * Adds an existing {@link Roo.TabPanelItem}.
10868      * @param {Roo.TabPanelItem} item The TabPanelItem to add
10869      */
10870     addTabItem : function(item){
10871         this.items[item.id] = item;
10872         this.items.push(item);
10873         if(this.resizeTabs){
10874            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10875            this.autoSizeTabs();
10876         }else{
10877             item.autoSize();
10878         }
10879     },
10880
10881     /**
10882      * Removes a {@link Roo.TabPanelItem}.
10883      * @param {String/Number} id The id or index of the TabPanelItem to remove.
10884      */
10885     removeTab : function(id){
10886         var items = this.items;
10887         var tab = items[id];
10888         if(!tab) { return; }
10889         var index = items.indexOf(tab);
10890         if(this.active == tab && items.length > 1){
10891             var newTab = this.getNextAvailable(index);
10892             if(newTab) {
10893                 newTab.activate();
10894             }
10895         }
10896         this.stripEl.dom.removeChild(tab.pnode.dom);
10897         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
10898             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
10899         }
10900         items.splice(index, 1);
10901         delete this.items[tab.id];
10902         tab.fireEvent("close", tab);
10903         tab.purgeListeners();
10904         this.autoSizeTabs();
10905     },
10906
10907     getNextAvailable : function(start){
10908         var items = this.items;
10909         var index = start;
10910         // look for a next tab that will slide over to
10911         // replace the one being removed
10912         while(index < items.length){
10913             var item = items[++index];
10914             if(item && !item.isHidden()){
10915                 return item;
10916             }
10917         }
10918         // if one isn't found select the previous tab (on the left)
10919         index = start;
10920         while(index >= 0){
10921             var item = items[--index];
10922             if(item && !item.isHidden()){
10923                 return item;
10924             }
10925         }
10926         return null;
10927     },
10928
10929     /**
10930      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
10931      * @param {String/Number} id The id or index of the TabPanelItem to disable.
10932      */
10933     disableTab : function(id){
10934         var tab = this.items[id];
10935         if(tab && this.active != tab){
10936             tab.disable();
10937         }
10938     },
10939
10940     /**
10941      * Enables a {@link Roo.TabPanelItem} that is disabled.
10942      * @param {String/Number} id The id or index of the TabPanelItem to enable.
10943      */
10944     enableTab : function(id){
10945         var tab = this.items[id];
10946         tab.enable();
10947     },
10948
10949     /**
10950      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
10951      * @param {String/Number} id The id or index of the TabPanelItem to activate.
10952      * @return {Roo.TabPanelItem} The TabPanelItem.
10953      */
10954     activate : function(id){
10955         var tab = this.items[id];
10956         if(!tab){
10957             return null;
10958         }
10959         if(tab == this.active || tab.disabled){
10960             return tab;
10961         }
10962         var e = {};
10963         this.fireEvent("beforetabchange", this, e, tab);
10964         if(e.cancel !== true && !tab.disabled){
10965             if(this.active){
10966                 this.active.hide();
10967             }
10968             this.active = this.items[id];
10969             this.active.show();
10970             this.fireEvent("tabchange", this, this.active);
10971         }
10972         return tab;
10973     },
10974
10975     /**
10976      * Gets the active {@link Roo.TabPanelItem}.
10977      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
10978      */
10979     getActiveTab : function(){
10980         return this.active;
10981     },
10982
10983     /**
10984      * Updates the tab body element to fit the height of the container element
10985      * for overflow scrolling
10986      * @param {Number} targetHeight (optional) Override the starting height from the elements height
10987      */
10988     syncHeight : function(targetHeight){
10989         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
10990         var bm = this.bodyEl.getMargins();
10991         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
10992         this.bodyEl.setHeight(newHeight);
10993         return newHeight;
10994     },
10995
10996     onResize : function(){
10997         if(this.monitorResize){
10998             this.autoSizeTabs();
10999         }
11000     },
11001
11002     /**
11003      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
11004      */
11005     beginUpdate : function(){
11006         this.updating = true;
11007     },
11008
11009     /**
11010      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
11011      */
11012     endUpdate : function(){
11013         this.updating = false;
11014         this.autoSizeTabs();
11015     },
11016
11017     /**
11018      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
11019      */
11020     autoSizeTabs : function(){
11021         var count = this.items.length;
11022         var vcount = count - this.hiddenCount;
11023         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
11024         var w = Math.max(this.el.getWidth() - this.cpad, 10);
11025         var availWidth = Math.floor(w / vcount);
11026         var b = this.stripBody;
11027         if(b.getWidth() > w){
11028             var tabs = this.items;
11029             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
11030             if(availWidth < this.minTabWidth){
11031                 /*if(!this.sleft){    // incomplete scrolling code
11032                     this.createScrollButtons();
11033                 }
11034                 this.showScroll();
11035                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
11036             }
11037         }else{
11038             if(this.currentTabWidth < this.preferredTabWidth){
11039                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
11040             }
11041         }
11042     },
11043
11044     /**
11045      * Returns the number of tabs in this TabPanel.
11046      * @return {Number}
11047      */
11048      getCount : function(){
11049          return this.items.length;
11050      },
11051
11052     /**
11053      * Resizes all the tabs to the passed width
11054      * @param {Number} The new width
11055      */
11056     setTabWidth : function(width){
11057         this.currentTabWidth = width;
11058         for(var i = 0, len = this.items.length; i < len; i++) {
11059                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
11060         }
11061     },
11062
11063     /**
11064      * Destroys this TabPanel
11065      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
11066      */
11067     destroy : function(removeEl){
11068         Roo.EventManager.removeResizeListener(this.onResize, this);
11069         for(var i = 0, len = this.items.length; i < len; i++){
11070             this.items[i].purgeListeners();
11071         }
11072         if(removeEl === true){
11073             this.el.update("");
11074             this.el.remove();
11075         }
11076     }
11077 });
11078
11079 /**
11080  * @class Roo.TabPanelItem
11081  * @extends Roo.util.Observable
11082  * Represents an individual item (tab plus body) in a TabPanel.
11083  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11084  * @param {String} id The id of this TabPanelItem
11085  * @param {String} text The text for the tab of this TabPanelItem
11086  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11087  */
11088 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11089     /**
11090      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11091      * @type Roo.TabPanel
11092      */
11093     this.tabPanel = tabPanel;
11094     /**
11095      * The id for this TabPanelItem
11096      * @type String
11097      */
11098     this.id = id;
11099     /** @private */
11100     this.disabled = false;
11101     /** @private */
11102     this.text = text;
11103     /** @private */
11104     this.loaded = false;
11105     this.closable = closable;
11106
11107     /**
11108      * The body element for this TabPanelItem.
11109      * @type Roo.Element
11110      */
11111     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11112     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11113     this.bodyEl.setStyle("display", "block");
11114     this.bodyEl.setStyle("zoom", "1");
11115     this.hideAction();
11116
11117     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11118     /** @private */
11119     this.el = Roo.get(els.el, true);
11120     this.inner = Roo.get(els.inner, true);
11121     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11122     this.pnode = Roo.get(els.el.parentNode, true);
11123     this.el.on("mousedown", this.onTabMouseDown, this);
11124     this.el.on("click", this.onTabClick, this);
11125     /** @private */
11126     if(closable){
11127         var c = Roo.get(els.close, true);
11128         c.dom.title = this.closeText;
11129         c.addClassOnOver("close-over");
11130         c.on("click", this.closeClick, this);
11131      }
11132
11133     this.addEvents({
11134          /**
11135          * @event activate
11136          * Fires when this tab becomes the active tab.
11137          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11138          * @param {Roo.TabPanelItem} this
11139          */
11140         "activate": true,
11141         /**
11142          * @event beforeclose
11143          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11144          * @param {Roo.TabPanelItem} this
11145          * @param {Object} e Set cancel to true on this object to cancel the close.
11146          */
11147         "beforeclose": true,
11148         /**
11149          * @event close
11150          * Fires when this tab is closed.
11151          * @param {Roo.TabPanelItem} this
11152          */
11153          "close": true,
11154         /**
11155          * @event deactivate
11156          * Fires when this tab is no longer the active tab.
11157          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11158          * @param {Roo.TabPanelItem} this
11159          */
11160          "deactivate" : true
11161     });
11162     this.hidden = false;
11163
11164     Roo.TabPanelItem.superclass.constructor.call(this);
11165 };
11166
11167 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11168     purgeListeners : function(){
11169        Roo.util.Observable.prototype.purgeListeners.call(this);
11170        this.el.removeAllListeners();
11171     },
11172     /**
11173      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11174      */
11175     show : function(){
11176         this.pnode.addClass("on");
11177         this.showAction();
11178         if(Roo.isOpera){
11179             this.tabPanel.stripWrap.repaint();
11180         }
11181         this.fireEvent("activate", this.tabPanel, this);
11182     },
11183
11184     /**
11185      * Returns true if this tab is the active tab.
11186      * @return {Boolean}
11187      */
11188     isActive : function(){
11189         return this.tabPanel.getActiveTab() == this;
11190     },
11191
11192     /**
11193      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11194      */
11195     hide : function(){
11196         this.pnode.removeClass("on");
11197         this.hideAction();
11198         this.fireEvent("deactivate", this.tabPanel, this);
11199     },
11200
11201     hideAction : function(){
11202         this.bodyEl.hide();
11203         this.bodyEl.setStyle("position", "absolute");
11204         this.bodyEl.setLeft("-20000px");
11205         this.bodyEl.setTop("-20000px");
11206     },
11207
11208     showAction : function(){
11209         this.bodyEl.setStyle("position", "relative");
11210         this.bodyEl.setTop("");
11211         this.bodyEl.setLeft("");
11212         this.bodyEl.show();
11213     },
11214
11215     /**
11216      * Set the tooltip for the tab.
11217      * @param {String} tooltip The tab's tooltip
11218      */
11219     setTooltip : function(text){
11220         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11221             this.textEl.dom.qtip = text;
11222             this.textEl.dom.removeAttribute('title');
11223         }else{
11224             this.textEl.dom.title = text;
11225         }
11226     },
11227
11228     onTabClick : function(e){
11229         e.preventDefault();
11230         this.tabPanel.activate(this.id);
11231     },
11232
11233     onTabMouseDown : function(e){
11234         e.preventDefault();
11235         this.tabPanel.activate(this.id);
11236     },
11237
11238     getWidth : function(){
11239         return this.inner.getWidth();
11240     },
11241
11242     setWidth : function(width){
11243         var iwidth = width - this.pnode.getPadding("lr");
11244         this.inner.setWidth(iwidth);
11245         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11246         this.pnode.setWidth(width);
11247     },
11248
11249     /**
11250      * Show or hide the tab
11251      * @param {Boolean} hidden True to hide or false to show.
11252      */
11253     setHidden : function(hidden){
11254         this.hidden = hidden;
11255         this.pnode.setStyle("display", hidden ? "none" : "");
11256     },
11257
11258     /**
11259      * Returns true if this tab is "hidden"
11260      * @return {Boolean}
11261      */
11262     isHidden : function(){
11263         return this.hidden;
11264     },
11265
11266     /**
11267      * Returns the text for this tab
11268      * @return {String}
11269      */
11270     getText : function(){
11271         return this.text;
11272     },
11273
11274     autoSize : function(){
11275         //this.el.beginMeasure();
11276         this.textEl.setWidth(1);
11277         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11278         //this.el.endMeasure();
11279     },
11280
11281     /**
11282      * Sets the text for the tab (Note: this also sets the tooltip text)
11283      * @param {String} text The tab's text and tooltip
11284      */
11285     setText : function(text){
11286         this.text = text;
11287         this.textEl.update(text);
11288         this.setTooltip(text);
11289         if(!this.tabPanel.resizeTabs){
11290             this.autoSize();
11291         }
11292     },
11293     /**
11294      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11295      */
11296     activate : function(){
11297         this.tabPanel.activate(this.id);
11298     },
11299
11300     /**
11301      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11302      */
11303     disable : function(){
11304         if(this.tabPanel.active != this){
11305             this.disabled = true;
11306             this.pnode.addClass("disabled");
11307         }
11308     },
11309
11310     /**
11311      * Enables this TabPanelItem if it was previously disabled.
11312      */
11313     enable : function(){
11314         this.disabled = false;
11315         this.pnode.removeClass("disabled");
11316     },
11317
11318     /**
11319      * Sets the content for this TabPanelItem.
11320      * @param {String} content The content
11321      * @param {Boolean} loadScripts true to look for and load scripts
11322      */
11323     setContent : function(content, loadScripts){
11324         this.bodyEl.update(content, loadScripts);
11325     },
11326
11327     /**
11328      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11329      * @return {Roo.UpdateManager} The UpdateManager
11330      */
11331     getUpdateManager : function(){
11332         return this.bodyEl.getUpdateManager();
11333     },
11334
11335     /**
11336      * Set a URL to be used to load the content for this TabPanelItem.
11337      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11338      * @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)
11339      * @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)
11340      * @return {Roo.UpdateManager} The UpdateManager
11341      */
11342     setUrl : function(url, params, loadOnce){
11343         if(this.refreshDelegate){
11344             this.un('activate', this.refreshDelegate);
11345         }
11346         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11347         this.on("activate", this.refreshDelegate);
11348         return this.bodyEl.getUpdateManager();
11349     },
11350
11351     /** @private */
11352     _handleRefresh : function(url, params, loadOnce){
11353         if(!loadOnce || !this.loaded){
11354             var updater = this.bodyEl.getUpdateManager();
11355             updater.update(url, params, this._setLoaded.createDelegate(this));
11356         }
11357     },
11358
11359     /**
11360      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11361      *   Will fail silently if the setUrl method has not been called.
11362      *   This does not activate the panel, just updates its content.
11363      */
11364     refresh : function(){
11365         if(this.refreshDelegate){
11366            this.loaded = false;
11367            this.refreshDelegate();
11368         }
11369     },
11370
11371     /** @private */
11372     _setLoaded : function(){
11373         this.loaded = true;
11374     },
11375
11376     /** @private */
11377     closeClick : function(e){
11378         var o = {};
11379         e.stopEvent();
11380         this.fireEvent("beforeclose", this, o);
11381         if(o.cancel !== true){
11382             this.tabPanel.removeTab(this.id);
11383         }
11384     },
11385     /**
11386      * The text displayed in the tooltip for the close icon.
11387      * @type String
11388      */
11389     closeText : "Close this tab"
11390 });
11391
11392 /** @private */
11393 Roo.TabPanel.prototype.createStrip = function(container){
11394     var strip = document.createElement("div");
11395     strip.className = "x-tabs-wrap";
11396     container.appendChild(strip);
11397     return strip;
11398 };
11399 /** @private */
11400 Roo.TabPanel.prototype.createStripList = function(strip){
11401     // div wrapper for retard IE
11402     strip.innerHTML = '<div class="x-tabs-strip-wrap"><table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr></tr></tbody></table></div>';
11403     return strip.firstChild.firstChild.firstChild.firstChild;
11404 };
11405 /** @private */
11406 Roo.TabPanel.prototype.createBody = function(container){
11407     var body = document.createElement("div");
11408     Roo.id(body, "tab-body");
11409     Roo.fly(body).addClass("x-tabs-body");
11410     container.appendChild(body);
11411     return body;
11412 };
11413 /** @private */
11414 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11415     var body = Roo.getDom(id);
11416     if(!body){
11417         body = document.createElement("div");
11418         body.id = id;
11419     }
11420     Roo.fly(body).addClass("x-tabs-item-body");
11421     bodyEl.insertBefore(body, bodyEl.firstChild);
11422     return body;
11423 };
11424 /** @private */
11425 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11426     var td = document.createElement("td");
11427     stripEl.appendChild(td);
11428     if(closable){
11429         td.className = "x-tabs-closable";
11430         if(!this.closeTpl){
11431             this.closeTpl = new Roo.Template(
11432                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11433                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11434                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11435             );
11436         }
11437         var el = this.closeTpl.overwrite(td, {"text": text});
11438         var close = el.getElementsByTagName("div")[0];
11439         var inner = el.getElementsByTagName("em")[0];
11440         return {"el": el, "close": close, "inner": inner};
11441     } else {
11442         if(!this.tabTpl){
11443             this.tabTpl = new Roo.Template(
11444                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11445                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11446             );
11447         }
11448         var el = this.tabTpl.overwrite(td, {"text": text});
11449         var inner = el.getElementsByTagName("em")[0];
11450         return {"el": el, "inner": inner};
11451     }
11452 };/*
11453  * Based on:
11454  * Ext JS Library 1.1.1
11455  * Copyright(c) 2006-2007, Ext JS, LLC.
11456  *
11457  * Originally Released Under LGPL - original licence link has changed is not relivant.
11458  *
11459  * Fork - LGPL
11460  * <script type="text/javascript">
11461  */
11462
11463 /**
11464  * @class Roo.Button
11465  * @extends Roo.util.Observable
11466  * Simple Button class
11467  * @cfg {String} text The button text
11468  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11469  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11470  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11471  * @cfg {Object} scope The scope of the handler
11472  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11473  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11474  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11475  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11476  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11477  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11478    applies if enableToggle = true)
11479  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11480  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11481   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11482  * @constructor
11483  * Create a new button
11484  * @param {Object} config The config object
11485  */
11486 Roo.Button = function(renderTo, config)
11487 {
11488     if (!config) {
11489         config = renderTo;
11490         renderTo = config.renderTo || false;
11491     }
11492     
11493     Roo.apply(this, config);
11494     this.addEvents({
11495         /**
11496              * @event click
11497              * Fires when this button is clicked
11498              * @param {Button} this
11499              * @param {EventObject} e The click event
11500              */
11501             "click" : true,
11502         /**
11503              * @event toggle
11504              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11505              * @param {Button} this
11506              * @param {Boolean} pressed
11507              */
11508             "toggle" : true,
11509         /**
11510              * @event mouseover
11511              * Fires when the mouse hovers over the button
11512              * @param {Button} this
11513              * @param {Event} e The event object
11514              */
11515         'mouseover' : true,
11516         /**
11517              * @event mouseout
11518              * Fires when the mouse exits the button
11519              * @param {Button} this
11520              * @param {Event} e The event object
11521              */
11522         'mouseout': true,
11523          /**
11524              * @event render
11525              * Fires when the button is rendered
11526              * @param {Button} this
11527              */
11528         'render': true
11529     });
11530     if(this.menu){
11531         this.menu = Roo.menu.MenuMgr.get(this.menu);
11532     }
11533     // register listeners first!!  - so render can be captured..
11534     Roo.util.Observable.call(this);
11535     if(renderTo){
11536         this.render(renderTo);
11537     }
11538     
11539   
11540 };
11541
11542 Roo.extend(Roo.Button, Roo.util.Observable, {
11543     /**
11544      * 
11545      */
11546     
11547     /**
11548      * Read-only. True if this button is hidden
11549      * @type Boolean
11550      */
11551     hidden : false,
11552     /**
11553      * Read-only. True if this button is disabled
11554      * @type Boolean
11555      */
11556     disabled : false,
11557     /**
11558      * Read-only. True if this button is pressed (only if enableToggle = true)
11559      * @type Boolean
11560      */
11561     pressed : false,
11562
11563     /**
11564      * @cfg {Number} tabIndex 
11565      * The DOM tabIndex for this button (defaults to undefined)
11566      */
11567     tabIndex : undefined,
11568
11569     /**
11570      * @cfg {Boolean} enableToggle
11571      * True to enable pressed/not pressed toggling (defaults to false)
11572      */
11573     enableToggle: false,
11574     /**
11575      * @cfg {Mixed} menu
11576      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11577      */
11578     menu : undefined,
11579     /**
11580      * @cfg {String} menuAlign
11581      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11582      */
11583     menuAlign : "tl-bl?",
11584
11585     /**
11586      * @cfg {String} iconCls
11587      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11588      */
11589     iconCls : undefined,
11590     /**
11591      * @cfg {String} type
11592      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11593      */
11594     type : 'button',
11595
11596     // private
11597     menuClassTarget: 'tr',
11598
11599     /**
11600      * @cfg {String} clickEvent
11601      * The type of event to map to the button's event handler (defaults to 'click')
11602      */
11603     clickEvent : 'click',
11604
11605     /**
11606      * @cfg {Boolean} handleMouseEvents
11607      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11608      */
11609     handleMouseEvents : true,
11610
11611     /**
11612      * @cfg {String} tooltipType
11613      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11614      */
11615     tooltipType : 'qtip',
11616
11617     /**
11618      * @cfg {String} cls
11619      * A CSS class to apply to the button's main element.
11620      */
11621     
11622     /**
11623      * @cfg {Roo.Template} template (Optional)
11624      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11625      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11626      * require code modifications if required elements (e.g. a button) aren't present.
11627      */
11628
11629     // private
11630     render : function(renderTo){
11631         var btn;
11632         if(this.hideParent){
11633             this.parentEl = Roo.get(renderTo);
11634         }
11635         if(!this.dhconfig){
11636             if(!this.template){
11637                 if(!Roo.Button.buttonTemplate){
11638                     // hideous table template
11639                     Roo.Button.buttonTemplate = new Roo.Template(
11640                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11641                         '<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>',
11642                         "</tr></tbody></table>");
11643                 }
11644                 this.template = Roo.Button.buttonTemplate;
11645             }
11646             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11647             var btnEl = btn.child("button:first");
11648             btnEl.on('focus', this.onFocus, this);
11649             btnEl.on('blur', this.onBlur, this);
11650             if(this.cls){
11651                 btn.addClass(this.cls);
11652             }
11653             if(this.icon){
11654                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11655             }
11656             if(this.iconCls){
11657                 btnEl.addClass(this.iconCls);
11658                 if(!this.cls){
11659                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11660                 }
11661             }
11662             if(this.tabIndex !== undefined){
11663                 btnEl.dom.tabIndex = this.tabIndex;
11664             }
11665             if(this.tooltip){
11666                 if(typeof this.tooltip == 'object'){
11667                     Roo.QuickTips.tips(Roo.apply({
11668                           target: btnEl.id
11669                     }, this.tooltip));
11670                 } else {
11671                     btnEl.dom[this.tooltipType] = this.tooltip;
11672                 }
11673             }
11674         }else{
11675             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11676         }
11677         this.el = btn;
11678         if(this.id){
11679             this.el.dom.id = this.el.id = this.id;
11680         }
11681         if(this.menu){
11682             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11683             this.menu.on("show", this.onMenuShow, this);
11684             this.menu.on("hide", this.onMenuHide, this);
11685         }
11686         btn.addClass("x-btn");
11687         if(Roo.isIE && !Roo.isIE7){
11688             this.autoWidth.defer(1, this);
11689         }else{
11690             this.autoWidth();
11691         }
11692         if(this.handleMouseEvents){
11693             btn.on("mouseover", this.onMouseOver, this);
11694             btn.on("mouseout", this.onMouseOut, this);
11695             btn.on("mousedown", this.onMouseDown, this);
11696         }
11697         btn.on(this.clickEvent, this.onClick, this);
11698         //btn.on("mouseup", this.onMouseUp, this);
11699         if(this.hidden){
11700             this.hide();
11701         }
11702         if(this.disabled){
11703             this.disable();
11704         }
11705         Roo.ButtonToggleMgr.register(this);
11706         if(this.pressed){
11707             this.el.addClass("x-btn-pressed");
11708         }
11709         if(this.repeat){
11710             var repeater = new Roo.util.ClickRepeater(btn,
11711                 typeof this.repeat == "object" ? this.repeat : {}
11712             );
11713             repeater.on("click", this.onClick,  this);
11714         }
11715         
11716         this.fireEvent('render', this);
11717         
11718     },
11719     /**
11720      * Returns the button's underlying element
11721      * @return {Roo.Element} The element
11722      */
11723     getEl : function(){
11724         return this.el;  
11725     },
11726     
11727     /**
11728      * Destroys this Button and removes any listeners.
11729      */
11730     destroy : function(){
11731         Roo.ButtonToggleMgr.unregister(this);
11732         this.el.removeAllListeners();
11733         this.purgeListeners();
11734         this.el.remove();
11735     },
11736
11737     // private
11738     autoWidth : function(){
11739         if(this.el){
11740             this.el.setWidth("auto");
11741             if(Roo.isIE7 && Roo.isStrict){
11742                 var ib = this.el.child('button');
11743                 if(ib && ib.getWidth() > 20){
11744                     ib.clip();
11745                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11746                 }
11747             }
11748             if(this.minWidth){
11749                 if(this.hidden){
11750                     this.el.beginMeasure();
11751                 }
11752                 if(this.el.getWidth() < this.minWidth){
11753                     this.el.setWidth(this.minWidth);
11754                 }
11755                 if(this.hidden){
11756                     this.el.endMeasure();
11757                 }
11758             }
11759         }
11760     },
11761
11762     /**
11763      * Assigns this button's click handler
11764      * @param {Function} handler The function to call when the button is clicked
11765      * @param {Object} scope (optional) Scope for the function passed in
11766      */
11767     setHandler : function(handler, scope){
11768         this.handler = handler;
11769         this.scope = scope;  
11770     },
11771     
11772     /**
11773      * Sets this button's text
11774      * @param {String} text The button text
11775      */
11776     setText : function(text){
11777         this.text = text;
11778         if(this.el){
11779             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11780         }
11781         this.autoWidth();
11782     },
11783     
11784     /**
11785      * Gets the text for this button
11786      * @return {String} The button text
11787      */
11788     getText : function(){
11789         return this.text;  
11790     },
11791     
11792     /**
11793      * Show this button
11794      */
11795     show: function(){
11796         this.hidden = false;
11797         if(this.el){
11798             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11799         }
11800     },
11801     
11802     /**
11803      * Hide this button
11804      */
11805     hide: function(){
11806         this.hidden = true;
11807         if(this.el){
11808             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11809         }
11810     },
11811     
11812     /**
11813      * Convenience function for boolean show/hide
11814      * @param {Boolean} visible True to show, false to hide
11815      */
11816     setVisible: function(visible){
11817         if(visible) {
11818             this.show();
11819         }else{
11820             this.hide();
11821         }
11822     },
11823     
11824     /**
11825      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11826      * @param {Boolean} state (optional) Force a particular state
11827      */
11828     toggle : function(state){
11829         state = state === undefined ? !this.pressed : state;
11830         if(state != this.pressed){
11831             if(state){
11832                 this.el.addClass("x-btn-pressed");
11833                 this.pressed = true;
11834                 this.fireEvent("toggle", this, true);
11835             }else{
11836                 this.el.removeClass("x-btn-pressed");
11837                 this.pressed = false;
11838                 this.fireEvent("toggle", this, false);
11839             }
11840             if(this.toggleHandler){
11841                 this.toggleHandler.call(this.scope || this, this, state);
11842             }
11843         }
11844     },
11845     
11846     /**
11847      * Focus the button
11848      */
11849     focus : function(){
11850         this.el.child('button:first').focus();
11851     },
11852     
11853     /**
11854      * Disable this button
11855      */
11856     disable : function(){
11857         if(this.el){
11858             this.el.addClass("x-btn-disabled");
11859         }
11860         this.disabled = true;
11861     },
11862     
11863     /**
11864      * Enable this button
11865      */
11866     enable : function(){
11867         if(this.el){
11868             this.el.removeClass("x-btn-disabled");
11869         }
11870         this.disabled = false;
11871     },
11872
11873     /**
11874      * Convenience function for boolean enable/disable
11875      * @param {Boolean} enabled True to enable, false to disable
11876      */
11877     setDisabled : function(v){
11878         this[v !== true ? "enable" : "disable"]();
11879     },
11880
11881     // private
11882     onClick : function(e){
11883         if(e){
11884             e.preventDefault();
11885         }
11886         if(e.button != 0){
11887             return;
11888         }
11889         if(!this.disabled){
11890             if(this.enableToggle){
11891                 this.toggle();
11892             }
11893             if(this.menu && !this.menu.isVisible()){
11894                 this.menu.show(this.el, this.menuAlign);
11895             }
11896             this.fireEvent("click", this, e);
11897             if(this.handler){
11898                 this.el.removeClass("x-btn-over");
11899                 this.handler.call(this.scope || this, this, e);
11900             }
11901         }
11902     },
11903     // private
11904     onMouseOver : function(e){
11905         if(!this.disabled){
11906             this.el.addClass("x-btn-over");
11907             this.fireEvent('mouseover', this, e);
11908         }
11909     },
11910     // private
11911     onMouseOut : function(e){
11912         if(!e.within(this.el,  true)){
11913             this.el.removeClass("x-btn-over");
11914             this.fireEvent('mouseout', this, e);
11915         }
11916     },
11917     // private
11918     onFocus : function(e){
11919         if(!this.disabled){
11920             this.el.addClass("x-btn-focus");
11921         }
11922     },
11923     // private
11924     onBlur : function(e){
11925         this.el.removeClass("x-btn-focus");
11926     },
11927     // private
11928     onMouseDown : function(e){
11929         if(!this.disabled && e.button == 0){
11930             this.el.addClass("x-btn-click");
11931             Roo.get(document).on('mouseup', this.onMouseUp, this);
11932         }
11933     },
11934     // private
11935     onMouseUp : function(e){
11936         if(e.button == 0){
11937             this.el.removeClass("x-btn-click");
11938             Roo.get(document).un('mouseup', this.onMouseUp, this);
11939         }
11940     },
11941     // private
11942     onMenuShow : function(e){
11943         this.el.addClass("x-btn-menu-active");
11944     },
11945     // private
11946     onMenuHide : function(e){
11947         this.el.removeClass("x-btn-menu-active");
11948     }   
11949 });
11950
11951 // Private utility class used by Button
11952 Roo.ButtonToggleMgr = function(){
11953    var groups = {};
11954    
11955    function toggleGroup(btn, state){
11956        if(state){
11957            var g = groups[btn.toggleGroup];
11958            for(var i = 0, l = g.length; i < l; i++){
11959                if(g[i] != btn){
11960                    g[i].toggle(false);
11961                }
11962            }
11963        }
11964    }
11965    
11966    return {
11967        register : function(btn){
11968            if(!btn.toggleGroup){
11969                return;
11970            }
11971            var g = groups[btn.toggleGroup];
11972            if(!g){
11973                g = groups[btn.toggleGroup] = [];
11974            }
11975            g.push(btn);
11976            btn.on("toggle", toggleGroup);
11977        },
11978        
11979        unregister : function(btn){
11980            if(!btn.toggleGroup){
11981                return;
11982            }
11983            var g = groups[btn.toggleGroup];
11984            if(g){
11985                g.remove(btn);
11986                btn.un("toggle", toggleGroup);
11987            }
11988        }
11989    };
11990 }();/*
11991  * Based on:
11992  * Ext JS Library 1.1.1
11993  * Copyright(c) 2006-2007, Ext JS, LLC.
11994  *
11995  * Originally Released Under LGPL - original licence link has changed is not relivant.
11996  *
11997  * Fork - LGPL
11998  * <script type="text/javascript">
11999  */
12000  
12001 /**
12002  * @class Roo.SplitButton
12003  * @extends Roo.Button
12004  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
12005  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
12006  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
12007  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
12008  * @cfg {String} arrowTooltip The title attribute of the arrow
12009  * @constructor
12010  * Create a new menu button
12011  * @param {String/HTMLElement/Element} renderTo The element to append the button to
12012  * @param {Object} config The config object
12013  */
12014 Roo.SplitButton = function(renderTo, config){
12015     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
12016     /**
12017      * @event arrowclick
12018      * Fires when this button's arrow is clicked
12019      * @param {SplitButton} this
12020      * @param {EventObject} e The click event
12021      */
12022     this.addEvents({"arrowclick":true});
12023 };
12024
12025 Roo.extend(Roo.SplitButton, Roo.Button, {
12026     render : function(renderTo){
12027         // this is one sweet looking template!
12028         var tpl = new Roo.Template(
12029             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
12030             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
12031             '<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>',
12032             "</tbody></table></td><td>",
12033             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
12034             '<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>',
12035             "</tbody></table></td></tr></table>"
12036         );
12037         var btn = tpl.append(renderTo, [this.text, this.type], true);
12038         var btnEl = btn.child("button");
12039         if(this.cls){
12040             btn.addClass(this.cls);
12041         }
12042         if(this.icon){
12043             btnEl.setStyle('background-image', 'url(' +this.icon +')');
12044         }
12045         if(this.iconCls){
12046             btnEl.addClass(this.iconCls);
12047             if(!this.cls){
12048                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
12049             }
12050         }
12051         this.el = btn;
12052         if(this.handleMouseEvents){
12053             btn.on("mouseover", this.onMouseOver, this);
12054             btn.on("mouseout", this.onMouseOut, this);
12055             btn.on("mousedown", this.onMouseDown, this);
12056             btn.on("mouseup", this.onMouseUp, this);
12057         }
12058         btn.on(this.clickEvent, this.onClick, this);
12059         if(this.tooltip){
12060             if(typeof this.tooltip == 'object'){
12061                 Roo.QuickTips.tips(Roo.apply({
12062                       target: btnEl.id
12063                 }, this.tooltip));
12064             } else {
12065                 btnEl.dom[this.tooltipType] = this.tooltip;
12066             }
12067         }
12068         if(this.arrowTooltip){
12069             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
12070         }
12071         if(this.hidden){
12072             this.hide();
12073         }
12074         if(this.disabled){
12075             this.disable();
12076         }
12077         if(this.pressed){
12078             this.el.addClass("x-btn-pressed");
12079         }
12080         if(Roo.isIE && !Roo.isIE7){
12081             this.autoWidth.defer(1, this);
12082         }else{
12083             this.autoWidth();
12084         }
12085         if(this.menu){
12086             this.menu.on("show", this.onMenuShow, this);
12087             this.menu.on("hide", this.onMenuHide, this);
12088         }
12089         this.fireEvent('render', this);
12090     },
12091
12092     // private
12093     autoWidth : function(){
12094         if(this.el){
12095             var tbl = this.el.child("table:first");
12096             var tbl2 = this.el.child("table:last");
12097             this.el.setWidth("auto");
12098             tbl.setWidth("auto");
12099             if(Roo.isIE7 && Roo.isStrict){
12100                 var ib = this.el.child('button:first');
12101                 if(ib && ib.getWidth() > 20){
12102                     ib.clip();
12103                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12104                 }
12105             }
12106             if(this.minWidth){
12107                 if(this.hidden){
12108                     this.el.beginMeasure();
12109                 }
12110                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12111                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12112                 }
12113                 if(this.hidden){
12114                     this.el.endMeasure();
12115                 }
12116             }
12117             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12118         } 
12119     },
12120     /**
12121      * Sets this button's click handler
12122      * @param {Function} handler The function to call when the button is clicked
12123      * @param {Object} scope (optional) Scope for the function passed above
12124      */
12125     setHandler : function(handler, scope){
12126         this.handler = handler;
12127         this.scope = scope;  
12128     },
12129     
12130     /**
12131      * Sets this button's arrow click handler
12132      * @param {Function} handler The function to call when the arrow is clicked
12133      * @param {Object} scope (optional) Scope for the function passed above
12134      */
12135     setArrowHandler : function(handler, scope){
12136         this.arrowHandler = handler;
12137         this.scope = scope;  
12138     },
12139     
12140     /**
12141      * Focus the button
12142      */
12143     focus : function(){
12144         if(this.el){
12145             this.el.child("button:first").focus();
12146         }
12147     },
12148
12149     // private
12150     onClick : function(e){
12151         e.preventDefault();
12152         if(!this.disabled){
12153             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12154                 if(this.menu && !this.menu.isVisible()){
12155                     this.menu.show(this.el, this.menuAlign);
12156                 }
12157                 this.fireEvent("arrowclick", this, e);
12158                 if(this.arrowHandler){
12159                     this.arrowHandler.call(this.scope || this, this, e);
12160                 }
12161             }else{
12162                 this.fireEvent("click", this, e);
12163                 if(this.handler){
12164                     this.handler.call(this.scope || this, this, e);
12165                 }
12166             }
12167         }
12168     },
12169     // private
12170     onMouseDown : function(e){
12171         if(!this.disabled){
12172             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12173         }
12174     },
12175     // private
12176     onMouseUp : function(e){
12177         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12178     }   
12179 });
12180
12181
12182 // backwards compat
12183 Roo.MenuButton = Roo.SplitButton;/*
12184  * Based on:
12185  * Ext JS Library 1.1.1
12186  * Copyright(c) 2006-2007, Ext JS, LLC.
12187  *
12188  * Originally Released Under LGPL - original licence link has changed is not relivant.
12189  *
12190  * Fork - LGPL
12191  * <script type="text/javascript">
12192  */
12193
12194 /**
12195  * @class Roo.Toolbar
12196  * Basic Toolbar class.
12197  * @constructor
12198  * Creates a new Toolbar
12199  * @param {Object} config The config object
12200  */ 
12201 Roo.Toolbar = function(container, buttons, config)
12202 {
12203     /// old consturctor format still supported..
12204     if(container instanceof Array){ // omit the container for later rendering
12205         buttons = container;
12206         config = buttons;
12207         container = null;
12208     }
12209     if (typeof(container) == 'object' && container.xtype) {
12210         config = container;
12211         container = config.container;
12212         buttons = config.buttons; // not really - use items!!
12213     }
12214     var xitems = [];
12215     if (config && config.items) {
12216         xitems = config.items;
12217         delete config.items;
12218     }
12219     Roo.apply(this, config);
12220     this.buttons = buttons;
12221     
12222     if(container){
12223         this.render(container);
12224     }
12225     Roo.each(xitems, function(b) {
12226         this.add(b);
12227     }, this);
12228     
12229 };
12230
12231 Roo.Toolbar.prototype = {
12232     /**
12233      * @cfg {Roo.data.Store} items
12234      * array of button configs or elements to add
12235      */
12236     
12237     /**
12238      * @cfg {String/HTMLElement/Element} container
12239      * The id or element that will contain the toolbar
12240      */
12241     // private
12242     render : function(ct){
12243         this.el = Roo.get(ct);
12244         if(this.cls){
12245             this.el.addClass(this.cls);
12246         }
12247         // using a table allows for vertical alignment
12248         // 100% width is needed by Safari...
12249         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12250         this.tr = this.el.child("tr", true);
12251         var autoId = 0;
12252         this.items = new Roo.util.MixedCollection(false, function(o){
12253             return o.id || ("item" + (++autoId));
12254         });
12255         if(this.buttons){
12256             this.add.apply(this, this.buttons);
12257             delete this.buttons;
12258         }
12259     },
12260
12261     /**
12262      * Adds element(s) to the toolbar -- this function takes a variable number of 
12263      * arguments of mixed type and adds them to the toolbar.
12264      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12265      * <ul>
12266      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12267      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12268      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12269      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12270      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12271      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12272      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12273      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12274      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12275      * </ul>
12276      * @param {Mixed} arg2
12277      * @param {Mixed} etc.
12278      */
12279     add : function(){
12280         var a = arguments, l = a.length;
12281         for(var i = 0; i < l; i++){
12282             this._add(a[i]);
12283         }
12284     },
12285     // private..
12286     _add : function(el) {
12287         
12288         if (el.xtype) {
12289             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12290         }
12291         
12292         if (el.applyTo){ // some kind of form field
12293             return this.addField(el);
12294         } 
12295         if (el.render){ // some kind of Toolbar.Item
12296             return this.addItem(el);
12297         }
12298         if (typeof el == "string"){ // string
12299             if(el == "separator" || el == "-"){
12300                 return this.addSeparator();
12301             }
12302             if (el == " "){
12303                 return this.addSpacer();
12304             }
12305             if(el == "->"){
12306                 return this.addFill();
12307             }
12308             return this.addText(el);
12309             
12310         }
12311         if(el.tagName){ // element
12312             return this.addElement(el);
12313         }
12314         if(typeof el == "object"){ // must be button config?
12315             return this.addButton(el);
12316         }
12317         // and now what?!?!
12318         return false;
12319         
12320     },
12321     
12322     /**
12323      * Add an Xtype element
12324      * @param {Object} xtype Xtype Object
12325      * @return {Object} created Object
12326      */
12327     addxtype : function(e){
12328         return this.add(e);  
12329     },
12330     
12331     /**
12332      * Returns the Element for this toolbar.
12333      * @return {Roo.Element}
12334      */
12335     getEl : function(){
12336         return this.el;  
12337     },
12338     
12339     /**
12340      * Adds a separator
12341      * @return {Roo.Toolbar.Item} The separator item
12342      */
12343     addSeparator : function(){
12344         return this.addItem(new Roo.Toolbar.Separator());
12345     },
12346
12347     /**
12348      * Adds a spacer element
12349      * @return {Roo.Toolbar.Spacer} The spacer item
12350      */
12351     addSpacer : function(){
12352         return this.addItem(new Roo.Toolbar.Spacer());
12353     },
12354
12355     /**
12356      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12357      * @return {Roo.Toolbar.Fill} The fill item
12358      */
12359     addFill : function(){
12360         return this.addItem(new Roo.Toolbar.Fill());
12361     },
12362
12363     /**
12364      * Adds any standard HTML element to the toolbar
12365      * @param {String/HTMLElement/Element} el The element or id of the element to add
12366      * @return {Roo.Toolbar.Item} The element's item
12367      */
12368     addElement : function(el){
12369         return this.addItem(new Roo.Toolbar.Item(el));
12370     },
12371     /**
12372      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12373      * @type Roo.util.MixedCollection  
12374      */
12375     items : false,
12376      
12377     /**
12378      * Adds any Toolbar.Item or subclass
12379      * @param {Roo.Toolbar.Item} item
12380      * @return {Roo.Toolbar.Item} The item
12381      */
12382     addItem : function(item){
12383         var td = this.nextBlock();
12384         item.render(td);
12385         this.items.add(item);
12386         return item;
12387     },
12388     
12389     /**
12390      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12391      * @param {Object/Array} config A button config or array of configs
12392      * @return {Roo.Toolbar.Button/Array}
12393      */
12394     addButton : function(config){
12395         if(config instanceof Array){
12396             var buttons = [];
12397             for(var i = 0, len = config.length; i < len; i++) {
12398                 buttons.push(this.addButton(config[i]));
12399             }
12400             return buttons;
12401         }
12402         var b = config;
12403         if(!(config instanceof Roo.Toolbar.Button)){
12404             b = config.split ?
12405                 new Roo.Toolbar.SplitButton(config) :
12406                 new Roo.Toolbar.Button(config);
12407         }
12408         var td = this.nextBlock();
12409         b.render(td);
12410         this.items.add(b);
12411         return b;
12412     },
12413     
12414     /**
12415      * Adds text to the toolbar
12416      * @param {String} text The text to add
12417      * @return {Roo.Toolbar.Item} The element's item
12418      */
12419     addText : function(text){
12420         return this.addItem(new Roo.Toolbar.TextItem(text));
12421     },
12422     
12423     /**
12424      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12425      * @param {Number} index The index where the item is to be inserted
12426      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12427      * @return {Roo.Toolbar.Button/Item}
12428      */
12429     insertButton : function(index, item){
12430         if(item instanceof Array){
12431             var buttons = [];
12432             for(var i = 0, len = item.length; i < len; i++) {
12433                buttons.push(this.insertButton(index + i, item[i]));
12434             }
12435             return buttons;
12436         }
12437         if (!(item instanceof Roo.Toolbar.Button)){
12438            item = new Roo.Toolbar.Button(item);
12439         }
12440         var td = document.createElement("td");
12441         this.tr.insertBefore(td, this.tr.childNodes[index]);
12442         item.render(td);
12443         this.items.insert(index, item);
12444         return item;
12445     },
12446     
12447     /**
12448      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12449      * @param {Object} config
12450      * @return {Roo.Toolbar.Item} The element's item
12451      */
12452     addDom : function(config, returnEl){
12453         var td = this.nextBlock();
12454         Roo.DomHelper.overwrite(td, config);
12455         var ti = new Roo.Toolbar.Item(td.firstChild);
12456         ti.render(td);
12457         this.items.add(ti);
12458         return ti;
12459     },
12460
12461     /**
12462      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12463      * @type Roo.util.MixedCollection  
12464      */
12465     fields : false,
12466     
12467     /**
12468      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
12469      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
12470      * @param {Roo.form.Field} field
12471      * @return {Roo.ToolbarItem}
12472      */
12473      
12474       
12475     addField : function(field) {
12476         if (!this.fields) {
12477             var autoId = 0;
12478             this.fields = new Roo.util.MixedCollection(false, function(o){
12479                 return o.id || ("item" + (++autoId));
12480             });
12481
12482         }
12483         
12484         var td = this.nextBlock();
12485         field.render(td);
12486         var ti = new Roo.Toolbar.Item(td.firstChild);
12487         ti.render(td);
12488         this.items.add(ti);
12489         this.fields.add(field);
12490         return ti;
12491     },
12492     /**
12493      * Hide the toolbar
12494      * @method hide
12495      */
12496      
12497       
12498     hide : function()
12499     {
12500         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12501         this.el.child('div').hide();
12502     },
12503     /**
12504      * Show the toolbar
12505      * @method show
12506      */
12507     show : function()
12508     {
12509         this.el.child('div').show();
12510     },
12511       
12512     // private
12513     nextBlock : function(){
12514         var td = document.createElement("td");
12515         this.tr.appendChild(td);
12516         return td;
12517     },
12518
12519     // private
12520     destroy : function(){
12521         if(this.items){ // rendered?
12522             Roo.destroy.apply(Roo, this.items.items);
12523         }
12524         if(this.fields){ // rendered?
12525             Roo.destroy.apply(Roo, this.fields.items);
12526         }
12527         Roo.Element.uncache(this.el, this.tr);
12528     }
12529 };
12530
12531 /**
12532  * @class Roo.Toolbar.Item
12533  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12534  * @constructor
12535  * Creates a new Item
12536  * @param {HTMLElement} el 
12537  */
12538 Roo.Toolbar.Item = function(el){
12539     this.el = Roo.getDom(el);
12540     this.id = Roo.id(this.el);
12541     this.hidden = false;
12542 };
12543
12544 Roo.Toolbar.Item.prototype = {
12545     
12546     /**
12547      * Get this item's HTML Element
12548      * @return {HTMLElement}
12549      */
12550     getEl : function(){
12551        return this.el;  
12552     },
12553
12554     // private
12555     render : function(td){
12556         this.td = td;
12557         td.appendChild(this.el);
12558     },
12559     
12560     /**
12561      * Removes and destroys this item.
12562      */
12563     destroy : function(){
12564         this.td.parentNode.removeChild(this.td);
12565     },
12566     
12567     /**
12568      * Shows this item.
12569      */
12570     show: function(){
12571         this.hidden = false;
12572         this.td.style.display = "";
12573     },
12574     
12575     /**
12576      * Hides this item.
12577      */
12578     hide: function(){
12579         this.hidden = true;
12580         this.td.style.display = "none";
12581     },
12582     
12583     /**
12584      * Convenience function for boolean show/hide.
12585      * @param {Boolean} visible true to show/false to hide
12586      */
12587     setVisible: function(visible){
12588         if(visible) {
12589             this.show();
12590         }else{
12591             this.hide();
12592         }
12593     },
12594     
12595     /**
12596      * Try to focus this item.
12597      */
12598     focus : function(){
12599         Roo.fly(this.el).focus();
12600     },
12601     
12602     /**
12603      * Disables this item.
12604      */
12605     disable : function(){
12606         Roo.fly(this.td).addClass("x-item-disabled");
12607         this.disabled = true;
12608         this.el.disabled = true;
12609     },
12610     
12611     /**
12612      * Enables this item.
12613      */
12614     enable : function(){
12615         Roo.fly(this.td).removeClass("x-item-disabled");
12616         this.disabled = false;
12617         this.el.disabled = false;
12618     }
12619 };
12620
12621
12622 /**
12623  * @class Roo.Toolbar.Separator
12624  * @extends Roo.Toolbar.Item
12625  * A simple toolbar separator class
12626  * @constructor
12627  * Creates a new Separator
12628  */
12629 Roo.Toolbar.Separator = function(){
12630     var s = document.createElement("span");
12631     s.className = "ytb-sep";
12632     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12633 };
12634 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12635     enable:Roo.emptyFn,
12636     disable:Roo.emptyFn,
12637     focus:Roo.emptyFn
12638 });
12639
12640 /**
12641  * @class Roo.Toolbar.Spacer
12642  * @extends Roo.Toolbar.Item
12643  * A simple element that adds extra horizontal space to a toolbar.
12644  * @constructor
12645  * Creates a new Spacer
12646  */
12647 Roo.Toolbar.Spacer = function(){
12648     var s = document.createElement("div");
12649     s.className = "ytb-spacer";
12650     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12651 };
12652 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12653     enable:Roo.emptyFn,
12654     disable:Roo.emptyFn,
12655     focus:Roo.emptyFn
12656 });
12657
12658 /**
12659  * @class Roo.Toolbar.Fill
12660  * @extends Roo.Toolbar.Spacer
12661  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12662  * @constructor
12663  * Creates a new Spacer
12664  */
12665 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12666     // private
12667     render : function(td){
12668         td.style.width = '100%';
12669         Roo.Toolbar.Fill.superclass.render.call(this, td);
12670     }
12671 });
12672
12673 /**
12674  * @class Roo.Toolbar.TextItem
12675  * @extends Roo.Toolbar.Item
12676  * A simple class that renders text directly into a toolbar.
12677  * @constructor
12678  * Creates a new TextItem
12679  * @param {String} text
12680  */
12681 Roo.Toolbar.TextItem = function(text){
12682     if (typeof(text) == 'object') {
12683         text = text.text;
12684     }
12685     var s = document.createElement("span");
12686     s.className = "ytb-text";
12687     s.innerHTML = text;
12688     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12689 };
12690 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12691     enable:Roo.emptyFn,
12692     disable:Roo.emptyFn,
12693     focus:Roo.emptyFn
12694 });
12695
12696 /**
12697  * @class Roo.Toolbar.Button
12698  * @extends Roo.Button
12699  * A button that renders into a toolbar.
12700  * @constructor
12701  * Creates a new Button
12702  * @param {Object} config A standard {@link Roo.Button} config object
12703  */
12704 Roo.Toolbar.Button = function(config){
12705     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12706 };
12707 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12708     render : function(td){
12709         this.td = td;
12710         Roo.Toolbar.Button.superclass.render.call(this, td);
12711     },
12712     
12713     /**
12714      * Removes and destroys this button
12715      */
12716     destroy : function(){
12717         Roo.Toolbar.Button.superclass.destroy.call(this);
12718         this.td.parentNode.removeChild(this.td);
12719     },
12720     
12721     /**
12722      * Shows this button
12723      */
12724     show: function(){
12725         this.hidden = false;
12726         this.td.style.display = "";
12727     },
12728     
12729     /**
12730      * Hides this button
12731      */
12732     hide: function(){
12733         this.hidden = true;
12734         this.td.style.display = "none";
12735     },
12736
12737     /**
12738      * Disables this item
12739      */
12740     disable : function(){
12741         Roo.fly(this.td).addClass("x-item-disabled");
12742         this.disabled = true;
12743     },
12744
12745     /**
12746      * Enables this item
12747      */
12748     enable : function(){
12749         Roo.fly(this.td).removeClass("x-item-disabled");
12750         this.disabled = false;
12751     }
12752 });
12753 // backwards compat
12754 Roo.ToolbarButton = Roo.Toolbar.Button;
12755
12756 /**
12757  * @class Roo.Toolbar.SplitButton
12758  * @extends Roo.SplitButton
12759  * A menu button that renders into a toolbar.
12760  * @constructor
12761  * Creates a new SplitButton
12762  * @param {Object} config A standard {@link Roo.SplitButton} config object
12763  */
12764 Roo.Toolbar.SplitButton = function(config){
12765     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12766 };
12767 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12768     render : function(td){
12769         this.td = td;
12770         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12771     },
12772     
12773     /**
12774      * Removes and destroys this button
12775      */
12776     destroy : function(){
12777         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12778         this.td.parentNode.removeChild(this.td);
12779     },
12780     
12781     /**
12782      * Shows this button
12783      */
12784     show: function(){
12785         this.hidden = false;
12786         this.td.style.display = "";
12787     },
12788     
12789     /**
12790      * Hides this button
12791      */
12792     hide: function(){
12793         this.hidden = true;
12794         this.td.style.display = "none";
12795     }
12796 });
12797
12798 // backwards compat
12799 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12800  * Based on:
12801  * Ext JS Library 1.1.1
12802  * Copyright(c) 2006-2007, Ext JS, LLC.
12803  *
12804  * Originally Released Under LGPL - original licence link has changed is not relivant.
12805  *
12806  * Fork - LGPL
12807  * <script type="text/javascript">
12808  */
12809  
12810 /**
12811  * @class Roo.PagingToolbar
12812  * @extends Roo.Toolbar
12813  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12814  * @constructor
12815  * Create a new PagingToolbar
12816  * @param {Object} config The config object
12817  */
12818 Roo.PagingToolbar = function(el, ds, config)
12819 {
12820     // old args format still supported... - xtype is prefered..
12821     if (typeof(el) == 'object' && el.xtype) {
12822         // created from xtype...
12823         config = el;
12824         ds = el.dataSource;
12825         el = config.container;
12826     }
12827     var items = [];
12828     if (config.items) {
12829         items = config.items;
12830         config.items = [];
12831     }
12832     
12833     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12834     this.ds = ds;
12835     this.cursor = 0;
12836     this.renderButtons(this.el);
12837     this.bind(ds);
12838     
12839     // supprot items array.
12840    
12841     Roo.each(items, function(e) {
12842         this.add(Roo.factory(e));
12843     },this);
12844     
12845 };
12846
12847 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12848     /**
12849      * @cfg {Roo.data.Store} dataSource
12850      * The underlying data store providing the paged data
12851      */
12852     /**
12853      * @cfg {String/HTMLElement/Element} container
12854      * container The id or element that will contain the toolbar
12855      */
12856     /**
12857      * @cfg {Boolean} displayInfo
12858      * True to display the displayMsg (defaults to false)
12859      */
12860     /**
12861      * @cfg {Number} pageSize
12862      * The number of records to display per page (defaults to 20)
12863      */
12864     pageSize: 20,
12865     /**
12866      * @cfg {String} displayMsg
12867      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12868      */
12869     displayMsg : 'Displaying {0} - {1} of {2}',
12870     /**
12871      * @cfg {String} emptyMsg
12872      * The message to display when no records are found (defaults to "No data to display")
12873      */
12874     emptyMsg : 'No data to display',
12875     /**
12876      * Customizable piece of the default paging text (defaults to "Page")
12877      * @type String
12878      */
12879     beforePageText : "Page",
12880     /**
12881      * Customizable piece of the default paging text (defaults to "of %0")
12882      * @type String
12883      */
12884     afterPageText : "of {0}",
12885     /**
12886      * Customizable piece of the default paging text (defaults to "First Page")
12887      * @type String
12888      */
12889     firstText : "First Page",
12890     /**
12891      * Customizable piece of the default paging text (defaults to "Previous Page")
12892      * @type String
12893      */
12894     prevText : "Previous Page",
12895     /**
12896      * Customizable piece of the default paging text (defaults to "Next Page")
12897      * @type String
12898      */
12899     nextText : "Next Page",
12900     /**
12901      * Customizable piece of the default paging text (defaults to "Last Page")
12902      * @type String
12903      */
12904     lastText : "Last Page",
12905     /**
12906      * Customizable piece of the default paging text (defaults to "Refresh")
12907      * @type String
12908      */
12909     refreshText : "Refresh",
12910
12911     // private
12912     renderButtons : function(el){
12913         Roo.PagingToolbar.superclass.render.call(this, el);
12914         this.first = this.addButton({
12915             tooltip: this.firstText,
12916             cls: "x-btn-icon x-grid-page-first",
12917             disabled: true,
12918             handler: this.onClick.createDelegate(this, ["first"])
12919         });
12920         this.prev = this.addButton({
12921             tooltip: this.prevText,
12922             cls: "x-btn-icon x-grid-page-prev",
12923             disabled: true,
12924             handler: this.onClick.createDelegate(this, ["prev"])
12925         });
12926         //this.addSeparator();
12927         this.add(this.beforePageText);
12928         this.field = Roo.get(this.addDom({
12929            tag: "input",
12930            type: "text",
12931            size: "3",
12932            value: "1",
12933            cls: "x-grid-page-number"
12934         }).el);
12935         this.field.on("keydown", this.onPagingKeydown, this);
12936         this.field.on("focus", function(){this.dom.select();});
12937         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
12938         this.field.setHeight(18);
12939         //this.addSeparator();
12940         this.next = this.addButton({
12941             tooltip: this.nextText,
12942             cls: "x-btn-icon x-grid-page-next",
12943             disabled: true,
12944             handler: this.onClick.createDelegate(this, ["next"])
12945         });
12946         this.last = this.addButton({
12947             tooltip: this.lastText,
12948             cls: "x-btn-icon x-grid-page-last",
12949             disabled: true,
12950             handler: this.onClick.createDelegate(this, ["last"])
12951         });
12952         //this.addSeparator();
12953         this.loading = this.addButton({
12954             tooltip: this.refreshText,
12955             cls: "x-btn-icon x-grid-loading",
12956             handler: this.onClick.createDelegate(this, ["refresh"])
12957         });
12958
12959         if(this.displayInfo){
12960             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
12961         }
12962     },
12963
12964     // private
12965     updateInfo : function(){
12966         if(this.displayEl){
12967             var count = this.ds.getCount();
12968             var msg = count == 0 ?
12969                 this.emptyMsg :
12970                 String.format(
12971                     this.displayMsg,
12972                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
12973                 );
12974             this.displayEl.update(msg);
12975         }
12976     },
12977
12978     // private
12979     onLoad : function(ds, r, o){
12980        this.cursor = o.params ? o.params.start : 0;
12981        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
12982
12983        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
12984        this.field.dom.value = ap;
12985        this.first.setDisabled(ap == 1);
12986        this.prev.setDisabled(ap == 1);
12987        this.next.setDisabled(ap == ps);
12988        this.last.setDisabled(ap == ps);
12989        this.loading.enable();
12990        this.updateInfo();
12991     },
12992
12993     // private
12994     getPageData : function(){
12995         var total = this.ds.getTotalCount();
12996         return {
12997             total : total,
12998             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
12999             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
13000         };
13001     },
13002
13003     // private
13004     onLoadError : function(){
13005         this.loading.enable();
13006     },
13007
13008     // private
13009     onPagingKeydown : function(e){
13010         var k = e.getKey();
13011         var d = this.getPageData();
13012         if(k == e.RETURN){
13013             var v = this.field.dom.value, pageNum;
13014             if(!v || isNaN(pageNum = parseInt(v, 10))){
13015                 this.field.dom.value = d.activePage;
13016                 return;
13017             }
13018             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
13019             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13020             e.stopEvent();
13021         }
13022         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))
13023         {
13024           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
13025           this.field.dom.value = pageNum;
13026           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13027           e.stopEvent();
13028         }
13029         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13030         {
13031           var v = this.field.dom.value, pageNum; 
13032           var increment = (e.shiftKey) ? 10 : 1;
13033           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13034             increment *= -1;
13035           if(!v || isNaN(pageNum = parseInt(v, 10))) {
13036             this.field.dom.value = d.activePage;
13037             return;
13038           }
13039           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13040           {
13041             this.field.dom.value = parseInt(v, 10) + increment;
13042             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13043             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13044           }
13045           e.stopEvent();
13046         }
13047     },
13048
13049     // private
13050     beforeLoad : function(){
13051         if(this.loading){
13052             this.loading.disable();
13053         }
13054     },
13055
13056     // private
13057     onClick : function(which){
13058         var ds = this.ds;
13059         switch(which){
13060             case "first":
13061                 ds.load({params:{start: 0, limit: this.pageSize}});
13062             break;
13063             case "prev":
13064                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13065             break;
13066             case "next":
13067                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13068             break;
13069             case "last":
13070                 var total = ds.getTotalCount();
13071                 var extra = total % this.pageSize;
13072                 var lastStart = extra ? (total - extra) : total-this.pageSize;
13073                 ds.load({params:{start: lastStart, limit: this.pageSize}});
13074             break;
13075             case "refresh":
13076                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13077             break;
13078         }
13079     },
13080
13081     /**
13082      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13083      * @param {Roo.data.Store} store The data store to unbind
13084      */
13085     unbind : function(ds){
13086         ds.un("beforeload", this.beforeLoad, this);
13087         ds.un("load", this.onLoad, this);
13088         ds.un("loadexception", this.onLoadError, this);
13089         ds.un("remove", this.updateInfo, this);
13090         ds.un("add", this.updateInfo, this);
13091         this.ds = undefined;
13092     },
13093
13094     /**
13095      * Binds the paging toolbar to the specified {@link Roo.data.Store}
13096      * @param {Roo.data.Store} store The data store to bind
13097      */
13098     bind : function(ds){
13099         ds.on("beforeload", this.beforeLoad, this);
13100         ds.on("load", this.onLoad, this);
13101         ds.on("loadexception", this.onLoadError, this);
13102         ds.on("remove", this.updateInfo, this);
13103         ds.on("add", this.updateInfo, this);
13104         this.ds = ds;
13105     }
13106 });/*
13107  * Based on:
13108  * Ext JS Library 1.1.1
13109  * Copyright(c) 2006-2007, Ext JS, LLC.
13110  *
13111  * Originally Released Under LGPL - original licence link has changed is not relivant.
13112  *
13113  * Fork - LGPL
13114  * <script type="text/javascript">
13115  */
13116
13117 /**
13118  * @class Roo.Resizable
13119  * @extends Roo.util.Observable
13120  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13121  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13122  * 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
13123  * the element will be wrapped for you automatically.</p>
13124  * <p>Here is the list of valid resize handles:</p>
13125  * <pre>
13126 Value   Description
13127 ------  -------------------
13128  'n'     north
13129  's'     south
13130  'e'     east
13131  'w'     west
13132  'nw'    northwest
13133  'sw'    southwest
13134  'se'    southeast
13135  'ne'    northeast
13136  'hd'    horizontal drag
13137  'all'   all
13138 </pre>
13139  * <p>Here's an example showing the creation of a typical Resizable:</p>
13140  * <pre><code>
13141 var resizer = new Roo.Resizable("element-id", {
13142     handles: 'all',
13143     minWidth: 200,
13144     minHeight: 100,
13145     maxWidth: 500,
13146     maxHeight: 400,
13147     pinned: true
13148 });
13149 resizer.on("resize", myHandler);
13150 </code></pre>
13151  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13152  * resizer.east.setDisplayed(false);</p>
13153  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13154  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13155  * resize operation's new size (defaults to [0, 0])
13156  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13157  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13158  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13159  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13160  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13161  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13162  * @cfg {Number} width The width of the element in pixels (defaults to null)
13163  * @cfg {Number} height The height of the element in pixels (defaults to null)
13164  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13165  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13166  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13167  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13168  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13169  * in favor of the handles config option (defaults to false)
13170  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13171  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13172  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13173  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13174  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13175  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13176  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13177  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13178  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13179  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13180  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13181  * @constructor
13182  * Create a new resizable component
13183  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13184  * @param {Object} config configuration options
13185   */
13186 Roo.Resizable = function(el, config)
13187 {
13188     this.el = Roo.get(el);
13189
13190     if(config && config.wrap){
13191         config.resizeChild = this.el;
13192         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13193         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13194         this.el.setStyle("overflow", "hidden");
13195         this.el.setPositioning(config.resizeChild.getPositioning());
13196         config.resizeChild.clearPositioning();
13197         if(!config.width || !config.height){
13198             var csize = config.resizeChild.getSize();
13199             this.el.setSize(csize.width, csize.height);
13200         }
13201         if(config.pinned && !config.adjustments){
13202             config.adjustments = "auto";
13203         }
13204     }
13205
13206     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13207     this.proxy.unselectable();
13208     this.proxy.enableDisplayMode('block');
13209
13210     Roo.apply(this, config);
13211
13212     if(this.pinned){
13213         this.disableTrackOver = true;
13214         this.el.addClass("x-resizable-pinned");
13215     }
13216     // if the element isn't positioned, make it relative
13217     var position = this.el.getStyle("position");
13218     if(position != "absolute" && position != "fixed"){
13219         this.el.setStyle("position", "relative");
13220     }
13221     if(!this.handles){ // no handles passed, must be legacy style
13222         this.handles = 's,e,se';
13223         if(this.multiDirectional){
13224             this.handles += ',n,w';
13225         }
13226     }
13227     if(this.handles == "all"){
13228         this.handles = "n s e w ne nw se sw";
13229     }
13230     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13231     var ps = Roo.Resizable.positions;
13232     for(var i = 0, len = hs.length; i < len; i++){
13233         if(hs[i] && ps[hs[i]]){
13234             var pos = ps[hs[i]];
13235             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13236         }
13237     }
13238     // legacy
13239     this.corner = this.southeast;
13240     
13241     // updateBox = the box can move..
13242     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13243         this.updateBox = true;
13244     }
13245
13246     this.activeHandle = null;
13247
13248     if(this.resizeChild){
13249         if(typeof this.resizeChild == "boolean"){
13250             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13251         }else{
13252             this.resizeChild = Roo.get(this.resizeChild, true);
13253         }
13254     }
13255     
13256     if(this.adjustments == "auto"){
13257         var rc = this.resizeChild;
13258         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13259         if(rc && (hw || hn)){
13260             rc.position("relative");
13261             rc.setLeft(hw ? hw.el.getWidth() : 0);
13262             rc.setTop(hn ? hn.el.getHeight() : 0);
13263         }
13264         this.adjustments = [
13265             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13266             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13267         ];
13268     }
13269
13270     if(this.draggable){
13271         this.dd = this.dynamic ?
13272             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13273         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13274     }
13275
13276     // public events
13277     this.addEvents({
13278         /**
13279          * @event beforeresize
13280          * Fired before resize is allowed. Set enabled to false to cancel resize.
13281          * @param {Roo.Resizable} this
13282          * @param {Roo.EventObject} e The mousedown event
13283          */
13284         "beforeresize" : true,
13285         /**
13286          * @event resize
13287          * Fired after a resize.
13288          * @param {Roo.Resizable} this
13289          * @param {Number} width The new width
13290          * @param {Number} height The new height
13291          * @param {Roo.EventObject} e The mouseup event
13292          */
13293         "resize" : true
13294     });
13295
13296     if(this.width !== null && this.height !== null){
13297         this.resizeTo(this.width, this.height);
13298     }else{
13299         this.updateChildSize();
13300     }
13301     if(Roo.isIE){
13302         this.el.dom.style.zoom = 1;
13303     }
13304     Roo.Resizable.superclass.constructor.call(this);
13305 };
13306
13307 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13308         resizeChild : false,
13309         adjustments : [0, 0],
13310         minWidth : 5,
13311         minHeight : 5,
13312         maxWidth : 10000,
13313         maxHeight : 10000,
13314         enabled : true,
13315         animate : false,
13316         duration : .35,
13317         dynamic : false,
13318         handles : false,
13319         multiDirectional : false,
13320         disableTrackOver : false,
13321         easing : 'easeOutStrong',
13322         widthIncrement : 0,
13323         heightIncrement : 0,
13324         pinned : false,
13325         width : null,
13326         height : null,
13327         preserveRatio : false,
13328         transparent: false,
13329         minX: 0,
13330         minY: 0,
13331         draggable: false,
13332
13333         /**
13334          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13335          */
13336         constrainTo: undefined,
13337         /**
13338          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13339          */
13340         resizeRegion: undefined,
13341
13342
13343     /**
13344      * Perform a manual resize
13345      * @param {Number} width
13346      * @param {Number} height
13347      */
13348     resizeTo : function(width, height){
13349         this.el.setSize(width, height);
13350         this.updateChildSize();
13351         this.fireEvent("resize", this, width, height, null);
13352     },
13353
13354     // private
13355     startSizing : function(e, handle){
13356         this.fireEvent("beforeresize", this, e);
13357         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13358
13359             if(!this.overlay){
13360                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13361                 this.overlay.unselectable();
13362                 this.overlay.enableDisplayMode("block");
13363                 this.overlay.on("mousemove", this.onMouseMove, this);
13364                 this.overlay.on("mouseup", this.onMouseUp, this);
13365             }
13366             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13367
13368             this.resizing = true;
13369             this.startBox = this.el.getBox();
13370             this.startPoint = e.getXY();
13371             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13372                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13373
13374             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13375             this.overlay.show();
13376
13377             if(this.constrainTo) {
13378                 var ct = Roo.get(this.constrainTo);
13379                 this.resizeRegion = ct.getRegion().adjust(
13380                     ct.getFrameWidth('t'),
13381                     ct.getFrameWidth('l'),
13382                     -ct.getFrameWidth('b'),
13383                     -ct.getFrameWidth('r')
13384                 );
13385             }
13386
13387             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13388             this.proxy.show();
13389             this.proxy.setBox(this.startBox);
13390             if(!this.dynamic){
13391                 this.proxy.setStyle('visibility', 'visible');
13392             }
13393         }
13394     },
13395
13396     // private
13397     onMouseDown : function(handle, e){
13398         if(this.enabled){
13399             e.stopEvent();
13400             this.activeHandle = handle;
13401             this.startSizing(e, handle);
13402         }
13403     },
13404
13405     // private
13406     onMouseUp : function(e){
13407         var size = this.resizeElement();
13408         this.resizing = false;
13409         this.handleOut();
13410         this.overlay.hide();
13411         this.proxy.hide();
13412         this.fireEvent("resize", this, size.width, size.height, e);
13413     },
13414
13415     // private
13416     updateChildSize : function(){
13417         if(this.resizeChild){
13418             var el = this.el;
13419             var child = this.resizeChild;
13420             var adj = this.adjustments;
13421             if(el.dom.offsetWidth){
13422                 var b = el.getSize(true);
13423                 child.setSize(b.width+adj[0], b.height+adj[1]);
13424             }
13425             // Second call here for IE
13426             // The first call enables instant resizing and
13427             // the second call corrects scroll bars if they
13428             // exist
13429             if(Roo.isIE){
13430                 setTimeout(function(){
13431                     if(el.dom.offsetWidth){
13432                         var b = el.getSize(true);
13433                         child.setSize(b.width+adj[0], b.height+adj[1]);
13434                     }
13435                 }, 10);
13436             }
13437         }
13438     },
13439
13440     // private
13441     snap : function(value, inc, min){
13442         if(!inc || !value) return value;
13443         var newValue = value;
13444         var m = value % inc;
13445         if(m > 0){
13446             if(m > (inc/2)){
13447                 newValue = value + (inc-m);
13448             }else{
13449                 newValue = value - m;
13450             }
13451         }
13452         return Math.max(min, newValue);
13453     },
13454
13455     // private
13456     resizeElement : function(){
13457         var box = this.proxy.getBox();
13458         if(this.updateBox){
13459             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13460         }else{
13461             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13462         }
13463         this.updateChildSize();
13464         if(!this.dynamic){
13465             this.proxy.hide();
13466         }
13467         return box;
13468     },
13469
13470     // private
13471     constrain : function(v, diff, m, mx){
13472         if(v - diff < m){
13473             diff = v - m;
13474         }else if(v - diff > mx){
13475             diff = mx - v;
13476         }
13477         return diff;
13478     },
13479
13480     // private
13481     onMouseMove : function(e){
13482         if(this.enabled){
13483             try{// try catch so if something goes wrong the user doesn't get hung
13484
13485             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13486                 return;
13487             }
13488
13489             //var curXY = this.startPoint;
13490             var curSize = this.curSize || this.startBox;
13491             var x = this.startBox.x, y = this.startBox.y;
13492             var ox = x, oy = y;
13493             var w = curSize.width, h = curSize.height;
13494             var ow = w, oh = h;
13495             var mw = this.minWidth, mh = this.minHeight;
13496             var mxw = this.maxWidth, mxh = this.maxHeight;
13497             var wi = this.widthIncrement;
13498             var hi = this.heightIncrement;
13499
13500             var eventXY = e.getXY();
13501             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13502             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13503
13504             var pos = this.activeHandle.position;
13505
13506             switch(pos){
13507                 case "east":
13508                     w += diffX;
13509                     w = Math.min(Math.max(mw, w), mxw);
13510                     break;
13511              
13512                 case "south":
13513                     h += diffY;
13514                     h = Math.min(Math.max(mh, h), mxh);
13515                     break;
13516                 case "southeast":
13517                     w += diffX;
13518                     h += diffY;
13519                     w = Math.min(Math.max(mw, w), mxw);
13520                     h = Math.min(Math.max(mh, h), mxh);
13521                     break;
13522                 case "north":
13523                     diffY = this.constrain(h, diffY, mh, mxh);
13524                     y += diffY;
13525                     h -= diffY;
13526                     break;
13527                 case "hdrag":
13528                     
13529                     if (wi) {
13530                         var adiffX = Math.abs(diffX);
13531                         var sub = (adiffX % wi); // how much 
13532                         if (sub > (wi/2)) { // far enough to snap
13533                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13534                         } else {
13535                             // remove difference.. 
13536                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13537                         }
13538                     }
13539                     x += diffX;
13540                     x = Math.max(this.minX, x);
13541                     break;
13542                 case "west":
13543                     diffX = this.constrain(w, diffX, mw, mxw);
13544                     x += diffX;
13545                     w -= diffX;
13546                     break;
13547                 case "northeast":
13548                     w += diffX;
13549                     w = Math.min(Math.max(mw, w), mxw);
13550                     diffY = this.constrain(h, diffY, mh, mxh);
13551                     y += diffY;
13552                     h -= diffY;
13553                     break;
13554                 case "northwest":
13555                     diffX = this.constrain(w, diffX, mw, mxw);
13556                     diffY = this.constrain(h, diffY, mh, mxh);
13557                     y += diffY;
13558                     h -= diffY;
13559                     x += diffX;
13560                     w -= diffX;
13561                     break;
13562                case "southwest":
13563                     diffX = this.constrain(w, diffX, mw, mxw);
13564                     h += diffY;
13565                     h = Math.min(Math.max(mh, h), mxh);
13566                     x += diffX;
13567                     w -= diffX;
13568                     break;
13569             }
13570
13571             var sw = this.snap(w, wi, mw);
13572             var sh = this.snap(h, hi, mh);
13573             if(sw != w || sh != h){
13574                 switch(pos){
13575                     case "northeast":
13576                         y -= sh - h;
13577                     break;
13578                     case "north":
13579                         y -= sh - h;
13580                         break;
13581                     case "southwest":
13582                         x -= sw - w;
13583                     break;
13584                     case "west":
13585                         x -= sw - w;
13586                         break;
13587                     case "northwest":
13588                         x -= sw - w;
13589                         y -= sh - h;
13590                     break;
13591                 }
13592                 w = sw;
13593                 h = sh;
13594             }
13595
13596             if(this.preserveRatio){
13597                 switch(pos){
13598                     case "southeast":
13599                     case "east":
13600                         h = oh * (w/ow);
13601                         h = Math.min(Math.max(mh, h), mxh);
13602                         w = ow * (h/oh);
13603                        break;
13604                     case "south":
13605                         w = ow * (h/oh);
13606                         w = Math.min(Math.max(mw, w), mxw);
13607                         h = oh * (w/ow);
13608                         break;
13609                     case "northeast":
13610                         w = ow * (h/oh);
13611                         w = Math.min(Math.max(mw, w), mxw);
13612                         h = oh * (w/ow);
13613                     break;
13614                     case "north":
13615                         var tw = w;
13616                         w = ow * (h/oh);
13617                         w = Math.min(Math.max(mw, w), mxw);
13618                         h = oh * (w/ow);
13619                         x += (tw - w) / 2;
13620                         break;
13621                     case "southwest":
13622                         h = oh * (w/ow);
13623                         h = Math.min(Math.max(mh, h), mxh);
13624                         var tw = w;
13625                         w = ow * (h/oh);
13626                         x += tw - w;
13627                         break;
13628                     case "west":
13629                         var th = h;
13630                         h = oh * (w/ow);
13631                         h = Math.min(Math.max(mh, h), mxh);
13632                         y += (th - h) / 2;
13633                         var tw = w;
13634                         w = ow * (h/oh);
13635                         x += tw - w;
13636                        break;
13637                     case "northwest":
13638                         var tw = w;
13639                         var th = h;
13640                         h = oh * (w/ow);
13641                         h = Math.min(Math.max(mh, h), mxh);
13642                         w = ow * (h/oh);
13643                         y += th - h;
13644                         x += tw - w;
13645                        break;
13646
13647                 }
13648             }
13649             if (pos == 'hdrag') {
13650                 w = ow;
13651             }
13652             this.proxy.setBounds(x, y, w, h);
13653             if(this.dynamic){
13654                 this.resizeElement();
13655             }
13656             }catch(e){}
13657         }
13658     },
13659
13660     // private
13661     handleOver : function(){
13662         if(this.enabled){
13663             this.el.addClass("x-resizable-over");
13664         }
13665     },
13666
13667     // private
13668     handleOut : function(){
13669         if(!this.resizing){
13670             this.el.removeClass("x-resizable-over");
13671         }
13672     },
13673
13674     /**
13675      * Returns the element this component is bound to.
13676      * @return {Roo.Element}
13677      */
13678     getEl : function(){
13679         return this.el;
13680     },
13681
13682     /**
13683      * Returns the resizeChild element (or null).
13684      * @return {Roo.Element}
13685      */
13686     getResizeChild : function(){
13687         return this.resizeChild;
13688     },
13689
13690     /**
13691      * Destroys this resizable. If the element was wrapped and
13692      * removeEl is not true then the element remains.
13693      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13694      */
13695     destroy : function(removeEl){
13696         this.proxy.remove();
13697         if(this.overlay){
13698             this.overlay.removeAllListeners();
13699             this.overlay.remove();
13700         }
13701         var ps = Roo.Resizable.positions;
13702         for(var k in ps){
13703             if(typeof ps[k] != "function" && this[ps[k]]){
13704                 var h = this[ps[k]];
13705                 h.el.removeAllListeners();
13706                 h.el.remove();
13707             }
13708         }
13709         if(removeEl){
13710             this.el.update("");
13711             this.el.remove();
13712         }
13713     }
13714 });
13715
13716 // private
13717 // hash to map config positions to true positions
13718 Roo.Resizable.positions = {
13719     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13720     hd: "hdrag"
13721 };
13722
13723 // private
13724 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13725     if(!this.tpl){
13726         // only initialize the template if resizable is used
13727         var tpl = Roo.DomHelper.createTemplate(
13728             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13729         );
13730         tpl.compile();
13731         Roo.Resizable.Handle.prototype.tpl = tpl;
13732     }
13733     this.position = pos;
13734     this.rz = rz;
13735     // show north drag fro topdra
13736     var handlepos = pos == 'hdrag' ? 'north' : pos;
13737     
13738     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13739     if (pos == 'hdrag') {
13740         this.el.setStyle('cursor', 'pointer');
13741     }
13742     this.el.unselectable();
13743     if(transparent){
13744         this.el.setOpacity(0);
13745     }
13746     this.el.on("mousedown", this.onMouseDown, this);
13747     if(!disableTrackOver){
13748         this.el.on("mouseover", this.onMouseOver, this);
13749         this.el.on("mouseout", this.onMouseOut, this);
13750     }
13751 };
13752
13753 // private
13754 Roo.Resizable.Handle.prototype = {
13755     afterResize : function(rz){
13756         // do nothing
13757     },
13758     // private
13759     onMouseDown : function(e){
13760         this.rz.onMouseDown(this, e);
13761     },
13762     // private
13763     onMouseOver : function(e){
13764         this.rz.handleOver(this, e);
13765     },
13766     // private
13767     onMouseOut : function(e){
13768         this.rz.handleOut(this, e);
13769     }
13770 };/*
13771  * Based on:
13772  * Ext JS Library 1.1.1
13773  * Copyright(c) 2006-2007, Ext JS, LLC.
13774  *
13775  * Originally Released Under LGPL - original licence link has changed is not relivant.
13776  *
13777  * Fork - LGPL
13778  * <script type="text/javascript">
13779  */
13780
13781 /**
13782  * @class Roo.Editor
13783  * @extends Roo.Component
13784  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13785  * @constructor
13786  * Create a new Editor
13787  * @param {Roo.form.Field} field The Field object (or descendant)
13788  * @param {Object} config The config object
13789  */
13790 Roo.Editor = function(field, config){
13791     Roo.Editor.superclass.constructor.call(this, config);
13792     this.field = field;
13793     this.addEvents({
13794         /**
13795              * @event beforestartedit
13796              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13797              * false from the handler of this event.
13798              * @param {Editor} this
13799              * @param {Roo.Element} boundEl The underlying element bound to this editor
13800              * @param {Mixed} value The field value being set
13801              */
13802         "beforestartedit" : true,
13803         /**
13804              * @event startedit
13805              * Fires when this editor is displayed
13806              * @param {Roo.Element} boundEl The underlying element bound to this editor
13807              * @param {Mixed} value The starting field value
13808              */
13809         "startedit" : true,
13810         /**
13811              * @event beforecomplete
13812              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13813              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13814              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13815              * event will not fire since no edit actually occurred.
13816              * @param {Editor} this
13817              * @param {Mixed} value The current field value
13818              * @param {Mixed} startValue The original field value
13819              */
13820         "beforecomplete" : true,
13821         /**
13822              * @event complete
13823              * Fires after editing is complete and any changed value has been written to the underlying field.
13824              * @param {Editor} this
13825              * @param {Mixed} value The current field value
13826              * @param {Mixed} startValue The original field value
13827              */
13828         "complete" : true,
13829         /**
13830          * @event specialkey
13831          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13832          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13833          * @param {Roo.form.Field} this
13834          * @param {Roo.EventObject} e The event object
13835          */
13836         "specialkey" : true
13837     });
13838 };
13839
13840 Roo.extend(Roo.Editor, Roo.Component, {
13841     /**
13842      * @cfg {Boolean/String} autosize
13843      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13844      * or "height" to adopt the height only (defaults to false)
13845      */
13846     /**
13847      * @cfg {Boolean} revertInvalid
13848      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13849      * validation fails (defaults to true)
13850      */
13851     /**
13852      * @cfg {Boolean} ignoreNoChange
13853      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13854      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
13855      * will never be ignored.
13856      */
13857     /**
13858      * @cfg {Boolean} hideEl
13859      * False to keep the bound element visible while the editor is displayed (defaults to true)
13860      */
13861     /**
13862      * @cfg {Mixed} value
13863      * The data value of the underlying field (defaults to "")
13864      */
13865     value : "",
13866     /**
13867      * @cfg {String} alignment
13868      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13869      */
13870     alignment: "c-c?",
13871     /**
13872      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13873      * for bottom-right shadow (defaults to "frame")
13874      */
13875     shadow : "frame",
13876     /**
13877      * @cfg {Boolean} constrain True to constrain the editor to the viewport
13878      */
13879     constrain : false,
13880     /**
13881      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13882      */
13883     completeOnEnter : false,
13884     /**
13885      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
13886      */
13887     cancelOnEsc : false,
13888     /**
13889      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
13890      */
13891     updateEl : false,
13892
13893     // private
13894     onRender : function(ct, position){
13895         this.el = new Roo.Layer({
13896             shadow: this.shadow,
13897             cls: "x-editor",
13898             parentEl : ct,
13899             shim : this.shim,
13900             shadowOffset:4,
13901             id: this.id,
13902             constrain: this.constrain
13903         });
13904         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
13905         if(this.field.msgTarget != 'title'){
13906             this.field.msgTarget = 'qtip';
13907         }
13908         this.field.render(this.el);
13909         if(Roo.isGecko){
13910             this.field.el.dom.setAttribute('autocomplete', 'off');
13911         }
13912         this.field.on("specialkey", this.onSpecialKey, this);
13913         if(this.swallowKeys){
13914             this.field.el.swallowEvent(['keydown','keypress']);
13915         }
13916         this.field.show();
13917         this.field.on("blur", this.onBlur, this);
13918         if(this.field.grow){
13919             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
13920         }
13921     },
13922
13923     onSpecialKey : function(field, e)
13924     {
13925         //Roo.log('editor onSpecialKey');
13926         if(this.completeOnEnter && e.getKey() == e.ENTER){
13927             e.stopEvent();
13928             this.completeEdit();
13929             return;
13930         }
13931         // do not fire special key otherwise it might hide close the editor...
13932         if(e.getKey() == e.ENTER){    
13933             return;
13934         }
13935         if(this.cancelOnEsc && e.getKey() == e.ESC){
13936             this.cancelEdit();
13937             return;
13938         } 
13939         this.fireEvent('specialkey', field, e);
13940     
13941     },
13942
13943     /**
13944      * Starts the editing process and shows the editor.
13945      * @param {String/HTMLElement/Element} el The element to edit
13946      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
13947       * to the innerHTML of el.
13948      */
13949     startEdit : function(el, value){
13950         if(this.editing){
13951             this.completeEdit();
13952         }
13953         this.boundEl = Roo.get(el);
13954         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
13955         if(!this.rendered){
13956             this.render(this.parentEl || document.body);
13957         }
13958         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
13959             return;
13960         }
13961         this.startValue = v;
13962         this.field.setValue(v);
13963         if(this.autoSize){
13964             var sz = this.boundEl.getSize();
13965             switch(this.autoSize){
13966                 case "width":
13967                 this.setSize(sz.width,  "");
13968                 break;
13969                 case "height":
13970                 this.setSize("",  sz.height);
13971                 break;
13972                 default:
13973                 this.setSize(sz.width,  sz.height);
13974             }
13975         }
13976         this.el.alignTo(this.boundEl, this.alignment);
13977         this.editing = true;
13978         if(Roo.QuickTips){
13979             Roo.QuickTips.disable();
13980         }
13981         this.show();
13982     },
13983
13984     /**
13985      * Sets the height and width of this editor.
13986      * @param {Number} width The new width
13987      * @param {Number} height The new height
13988      */
13989     setSize : function(w, h){
13990         this.field.setSize(w, h);
13991         if(this.el){
13992             this.el.sync();
13993         }
13994     },
13995
13996     /**
13997      * Realigns the editor to the bound field based on the current alignment config value.
13998      */
13999     realign : function(){
14000         this.el.alignTo(this.boundEl, this.alignment);
14001     },
14002
14003     /**
14004      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
14005      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
14006      */
14007     completeEdit : function(remainVisible){
14008         if(!this.editing){
14009             return;
14010         }
14011         var v = this.getValue();
14012         if(this.revertInvalid !== false && !this.field.isValid()){
14013             v = this.startValue;
14014             this.cancelEdit(true);
14015         }
14016         if(String(v) === String(this.startValue) && this.ignoreNoChange){
14017             this.editing = false;
14018             this.hide();
14019             return;
14020         }
14021         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
14022             this.editing = false;
14023             if(this.updateEl && this.boundEl){
14024                 this.boundEl.update(v);
14025             }
14026             if(remainVisible !== true){
14027                 this.hide();
14028             }
14029             this.fireEvent("complete", this, v, this.startValue);
14030         }
14031     },
14032
14033     // private
14034     onShow : function(){
14035         this.el.show();
14036         if(this.hideEl !== false){
14037             this.boundEl.hide();
14038         }
14039         this.field.show();
14040         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14041             this.fixIEFocus = true;
14042             this.deferredFocus.defer(50, this);
14043         }else{
14044             this.field.focus();
14045         }
14046         this.fireEvent("startedit", this.boundEl, this.startValue);
14047     },
14048
14049     deferredFocus : function(){
14050         if(this.editing){
14051             this.field.focus();
14052         }
14053     },
14054
14055     /**
14056      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
14057      * reverted to the original starting value.
14058      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14059      * cancel (defaults to false)
14060      */
14061     cancelEdit : function(remainVisible){
14062         if(this.editing){
14063             this.setValue(this.startValue);
14064             if(remainVisible !== true){
14065                 this.hide();
14066             }
14067         }
14068     },
14069
14070     // private
14071     onBlur : function(){
14072         if(this.allowBlur !== true && this.editing){
14073             this.completeEdit();
14074         }
14075     },
14076
14077     // private
14078     onHide : function(){
14079         if(this.editing){
14080             this.completeEdit();
14081             return;
14082         }
14083         this.field.blur();
14084         if(this.field.collapse){
14085             this.field.collapse();
14086         }
14087         this.el.hide();
14088         if(this.hideEl !== false){
14089             this.boundEl.show();
14090         }
14091         if(Roo.QuickTips){
14092             Roo.QuickTips.enable();
14093         }
14094     },
14095
14096     /**
14097      * Sets the data value of the editor
14098      * @param {Mixed} value Any valid value supported by the underlying field
14099      */
14100     setValue : function(v){
14101         this.field.setValue(v);
14102     },
14103
14104     /**
14105      * Gets the data value of the editor
14106      * @return {Mixed} The data value
14107      */
14108     getValue : function(){
14109         return this.field.getValue();
14110     }
14111 });/*
14112  * Based on:
14113  * Ext JS Library 1.1.1
14114  * Copyright(c) 2006-2007, Ext JS, LLC.
14115  *
14116  * Originally Released Under LGPL - original licence link has changed is not relivant.
14117  *
14118  * Fork - LGPL
14119  * <script type="text/javascript">
14120  */
14121  
14122 /**
14123  * @class Roo.BasicDialog
14124  * @extends Roo.util.Observable
14125  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
14126  * <pre><code>
14127 var dlg = new Roo.BasicDialog("my-dlg", {
14128     height: 200,
14129     width: 300,
14130     minHeight: 100,
14131     minWidth: 150,
14132     modal: true,
14133     proxyDrag: true,
14134     shadow: true
14135 });
14136 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14137 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14138 dlg.addButton('Cancel', dlg.hide, dlg);
14139 dlg.show();
14140 </code></pre>
14141   <b>A Dialog should always be a direct child of the body element.</b>
14142  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14143  * @cfg {String} title Default text to display in the title bar (defaults to null)
14144  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14145  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14146  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14147  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14148  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14149  * (defaults to null with no animation)
14150  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14151  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14152  * property for valid values (defaults to 'all')
14153  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14154  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14155  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14156  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14157  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14158  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14159  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14160  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14161  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14162  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14163  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14164  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14165  * draggable = true (defaults to false)
14166  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14167  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14168  * shadow (defaults to false)
14169  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14170  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14171  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14172  * @cfg {Array} buttons Array of buttons
14173  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14174  * @constructor
14175  * Create a new BasicDialog.
14176  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14177  * @param {Object} config Configuration options
14178  */
14179 Roo.BasicDialog = function(el, config){
14180     this.el = Roo.get(el);
14181     var dh = Roo.DomHelper;
14182     if(!this.el && config && config.autoCreate){
14183         if(typeof config.autoCreate == "object"){
14184             if(!config.autoCreate.id){
14185                 config.autoCreate.id = el;
14186             }
14187             this.el = dh.append(document.body,
14188                         config.autoCreate, true);
14189         }else{
14190             this.el = dh.append(document.body,
14191                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14192         }
14193     }
14194     el = this.el;
14195     el.setDisplayed(true);
14196     el.hide = this.hideAction;
14197     this.id = el.id;
14198     el.addClass("x-dlg");
14199
14200     Roo.apply(this, config);
14201
14202     this.proxy = el.createProxy("x-dlg-proxy");
14203     this.proxy.hide = this.hideAction;
14204     this.proxy.setOpacity(.5);
14205     this.proxy.hide();
14206
14207     if(config.width){
14208         el.setWidth(config.width);
14209     }
14210     if(config.height){
14211         el.setHeight(config.height);
14212     }
14213     this.size = el.getSize();
14214     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14215         this.xy = [config.x,config.y];
14216     }else{
14217         this.xy = el.getCenterXY(true);
14218     }
14219     /** The header element @type Roo.Element */
14220     this.header = el.child("> .x-dlg-hd");
14221     /** The body element @type Roo.Element */
14222     this.body = el.child("> .x-dlg-bd");
14223     /** The footer element @type Roo.Element */
14224     this.footer = el.child("> .x-dlg-ft");
14225
14226     if(!this.header){
14227         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14228     }
14229     if(!this.body){
14230         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14231     }
14232
14233     this.header.unselectable();
14234     if(this.title){
14235         this.header.update(this.title);
14236     }
14237     // this element allows the dialog to be focused for keyboard event
14238     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14239     this.focusEl.swallowEvent("click", true);
14240
14241     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14242
14243     // wrap the body and footer for special rendering
14244     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14245     if(this.footer){
14246         this.bwrap.dom.appendChild(this.footer.dom);
14247     }
14248
14249     this.bg = this.el.createChild({
14250         tag: "div", cls:"x-dlg-bg",
14251         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14252     });
14253     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14254
14255
14256     if(this.autoScroll !== false && !this.autoTabs){
14257         this.body.setStyle("overflow", "auto");
14258     }
14259
14260     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14261
14262     if(this.closable !== false){
14263         this.el.addClass("x-dlg-closable");
14264         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14265         this.close.on("click", this.closeClick, this);
14266         this.close.addClassOnOver("x-dlg-close-over");
14267     }
14268     if(this.collapsible !== false){
14269         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14270         this.collapseBtn.on("click", this.collapseClick, this);
14271         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14272         this.header.on("dblclick", this.collapseClick, this);
14273     }
14274     if(this.resizable !== false){
14275         this.el.addClass("x-dlg-resizable");
14276         this.resizer = new Roo.Resizable(el, {
14277             minWidth: this.minWidth || 80,
14278             minHeight:this.minHeight || 80,
14279             handles: this.resizeHandles || "all",
14280             pinned: true
14281         });
14282         this.resizer.on("beforeresize", this.beforeResize, this);
14283         this.resizer.on("resize", this.onResize, this);
14284     }
14285     if(this.draggable !== false){
14286         el.addClass("x-dlg-draggable");
14287         if (!this.proxyDrag) {
14288             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14289         }
14290         else {
14291             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14292         }
14293         dd.setHandleElId(this.header.id);
14294         dd.endDrag = this.endMove.createDelegate(this);
14295         dd.startDrag = this.startMove.createDelegate(this);
14296         dd.onDrag = this.onDrag.createDelegate(this);
14297         dd.scroll = false;
14298         this.dd = dd;
14299     }
14300     if(this.modal){
14301         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14302         this.mask.enableDisplayMode("block");
14303         this.mask.hide();
14304         this.el.addClass("x-dlg-modal");
14305     }
14306     if(this.shadow){
14307         this.shadow = new Roo.Shadow({
14308             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14309             offset : this.shadowOffset
14310         });
14311     }else{
14312         this.shadowOffset = 0;
14313     }
14314     if(Roo.useShims && this.shim !== false){
14315         this.shim = this.el.createShim();
14316         this.shim.hide = this.hideAction;
14317         this.shim.hide();
14318     }else{
14319         this.shim = false;
14320     }
14321     if(this.autoTabs){
14322         this.initTabs();
14323     }
14324     if (this.buttons) { 
14325         var bts= this.buttons;
14326         this.buttons = [];
14327         Roo.each(bts, function(b) {
14328             this.addButton(b);
14329         }, this);
14330     }
14331     
14332     
14333     this.addEvents({
14334         /**
14335          * @event keydown
14336          * Fires when a key is pressed
14337          * @param {Roo.BasicDialog} this
14338          * @param {Roo.EventObject} e
14339          */
14340         "keydown" : true,
14341         /**
14342          * @event move
14343          * Fires when this dialog is moved by the user.
14344          * @param {Roo.BasicDialog} this
14345          * @param {Number} x The new page X
14346          * @param {Number} y The new page Y
14347          */
14348         "move" : true,
14349         /**
14350          * @event resize
14351          * Fires when this dialog is resized by the user.
14352          * @param {Roo.BasicDialog} this
14353          * @param {Number} width The new width
14354          * @param {Number} height The new height
14355          */
14356         "resize" : true,
14357         /**
14358          * @event beforehide
14359          * Fires before this dialog is hidden.
14360          * @param {Roo.BasicDialog} this
14361          */
14362         "beforehide" : true,
14363         /**
14364          * @event hide
14365          * Fires when this dialog is hidden.
14366          * @param {Roo.BasicDialog} this
14367          */
14368         "hide" : true,
14369         /**
14370          * @event beforeshow
14371          * Fires before this dialog is shown.
14372          * @param {Roo.BasicDialog} this
14373          */
14374         "beforeshow" : true,
14375         /**
14376          * @event show
14377          * Fires when this dialog is shown.
14378          * @param {Roo.BasicDialog} this
14379          */
14380         "show" : true
14381     });
14382     el.on("keydown", this.onKeyDown, this);
14383     el.on("mousedown", this.toFront, this);
14384     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14385     this.el.hide();
14386     Roo.DialogManager.register(this);
14387     Roo.BasicDialog.superclass.constructor.call(this);
14388 };
14389
14390 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14391     shadowOffset: Roo.isIE ? 6 : 5,
14392     minHeight: 80,
14393     minWidth: 200,
14394     minButtonWidth: 75,
14395     defaultButton: null,
14396     buttonAlign: "right",
14397     tabTag: 'div',
14398     firstShow: true,
14399
14400     /**
14401      * Sets the dialog title text
14402      * @param {String} text The title text to display
14403      * @return {Roo.BasicDialog} this
14404      */
14405     setTitle : function(text){
14406         this.header.update(text);
14407         return this;
14408     },
14409
14410     // private
14411     closeClick : function(){
14412         this.hide();
14413     },
14414
14415     // private
14416     collapseClick : function(){
14417         this[this.collapsed ? "expand" : "collapse"]();
14418     },
14419
14420     /**
14421      * Collapses the dialog to its minimized state (only the title bar is visible).
14422      * Equivalent to the user clicking the collapse dialog button.
14423      */
14424     collapse : function(){
14425         if(!this.collapsed){
14426             this.collapsed = true;
14427             this.el.addClass("x-dlg-collapsed");
14428             this.restoreHeight = this.el.getHeight();
14429             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14430         }
14431     },
14432
14433     /**
14434      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14435      * clicking the expand dialog button.
14436      */
14437     expand : function(){
14438         if(this.collapsed){
14439             this.collapsed = false;
14440             this.el.removeClass("x-dlg-collapsed");
14441             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14442         }
14443     },
14444
14445     /**
14446      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14447      * @return {Roo.TabPanel} The tabs component
14448      */
14449     initTabs : function(){
14450         var tabs = this.getTabs();
14451         while(tabs.getTab(0)){
14452             tabs.removeTab(0);
14453         }
14454         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14455             var dom = el.dom;
14456             tabs.addTab(Roo.id(dom), dom.title);
14457             dom.title = "";
14458         });
14459         tabs.activate(0);
14460         return tabs;
14461     },
14462
14463     // private
14464     beforeResize : function(){
14465         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14466     },
14467
14468     // private
14469     onResize : function(){
14470         this.refreshSize();
14471         this.syncBodyHeight();
14472         this.adjustAssets();
14473         this.focus();
14474         this.fireEvent("resize", this, this.size.width, this.size.height);
14475     },
14476
14477     // private
14478     onKeyDown : function(e){
14479         if(this.isVisible()){
14480             this.fireEvent("keydown", this, e);
14481         }
14482     },
14483
14484     /**
14485      * Resizes the dialog.
14486      * @param {Number} width
14487      * @param {Number} height
14488      * @return {Roo.BasicDialog} this
14489      */
14490     resizeTo : function(width, height){
14491         this.el.setSize(width, height);
14492         this.size = {width: width, height: height};
14493         this.syncBodyHeight();
14494         if(this.fixedcenter){
14495             this.center();
14496         }
14497         if(this.isVisible()){
14498             this.constrainXY();
14499             this.adjustAssets();
14500         }
14501         this.fireEvent("resize", this, width, height);
14502         return this;
14503     },
14504
14505
14506     /**
14507      * Resizes the dialog to fit the specified content size.
14508      * @param {Number} width
14509      * @param {Number} height
14510      * @return {Roo.BasicDialog} this
14511      */
14512     setContentSize : function(w, h){
14513         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14514         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14515         //if(!this.el.isBorderBox()){
14516             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14517             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14518         //}
14519         if(this.tabs){
14520             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14521             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14522         }
14523         this.resizeTo(w, h);
14524         return this;
14525     },
14526
14527     /**
14528      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14529      * executed in response to a particular key being pressed while the dialog is active.
14530      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14531      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14532      * @param {Function} fn The function to call
14533      * @param {Object} scope (optional) The scope of the function
14534      * @return {Roo.BasicDialog} this
14535      */
14536     addKeyListener : function(key, fn, scope){
14537         var keyCode, shift, ctrl, alt;
14538         if(typeof key == "object" && !(key instanceof Array)){
14539             keyCode = key["key"];
14540             shift = key["shift"];
14541             ctrl = key["ctrl"];
14542             alt = key["alt"];
14543         }else{
14544             keyCode = key;
14545         }
14546         var handler = function(dlg, e){
14547             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14548                 var k = e.getKey();
14549                 if(keyCode instanceof Array){
14550                     for(var i = 0, len = keyCode.length; i < len; i++){
14551                         if(keyCode[i] == k){
14552                           fn.call(scope || window, dlg, k, e);
14553                           return;
14554                         }
14555                     }
14556                 }else{
14557                     if(k == keyCode){
14558                         fn.call(scope || window, dlg, k, e);
14559                     }
14560                 }
14561             }
14562         };
14563         this.on("keydown", handler);
14564         return this;
14565     },
14566
14567     /**
14568      * Returns the TabPanel component (creates it if it doesn't exist).
14569      * Note: If you wish to simply check for the existence of tabs without creating them,
14570      * check for a null 'tabs' property.
14571      * @return {Roo.TabPanel} The tabs component
14572      */
14573     getTabs : function(){
14574         if(!this.tabs){
14575             this.el.addClass("x-dlg-auto-tabs");
14576             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14577             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14578         }
14579         return this.tabs;
14580     },
14581
14582     /**
14583      * Adds a button to the footer section of the dialog.
14584      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14585      * object or a valid Roo.DomHelper element config
14586      * @param {Function} handler The function called when the button is clicked
14587      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14588      * @return {Roo.Button} The new button
14589      */
14590     addButton : function(config, handler, scope){
14591         var dh = Roo.DomHelper;
14592         if(!this.footer){
14593             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14594         }
14595         if(!this.btnContainer){
14596             var tb = this.footer.createChild({
14597
14598                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14599                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14600             }, null, true);
14601             this.btnContainer = tb.firstChild.firstChild.firstChild;
14602         }
14603         var bconfig = {
14604             handler: handler,
14605             scope: scope,
14606             minWidth: this.minButtonWidth,
14607             hideParent:true
14608         };
14609         if(typeof config == "string"){
14610             bconfig.text = config;
14611         }else{
14612             if(config.tag){
14613                 bconfig.dhconfig = config;
14614             }else{
14615                 Roo.apply(bconfig, config);
14616             }
14617         }
14618         var fc = false;
14619         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14620             bconfig.position = Math.max(0, bconfig.position);
14621             fc = this.btnContainer.childNodes[bconfig.position];
14622         }
14623          
14624         var btn = new Roo.Button(
14625             fc ? 
14626                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14627                 : this.btnContainer.appendChild(document.createElement("td")),
14628             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14629             bconfig
14630         );
14631         this.syncBodyHeight();
14632         if(!this.buttons){
14633             /**
14634              * Array of all the buttons that have been added to this dialog via addButton
14635              * @type Array
14636              */
14637             this.buttons = [];
14638         }
14639         this.buttons.push(btn);
14640         return btn;
14641     },
14642
14643     /**
14644      * Sets the default button to be focused when the dialog is displayed.
14645      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14646      * @return {Roo.BasicDialog} this
14647      */
14648     setDefaultButton : function(btn){
14649         this.defaultButton = btn;
14650         return this;
14651     },
14652
14653     // private
14654     getHeaderFooterHeight : function(safe){
14655         var height = 0;
14656         if(this.header){
14657            height += this.header.getHeight();
14658         }
14659         if(this.footer){
14660            var fm = this.footer.getMargins();
14661             height += (this.footer.getHeight()+fm.top+fm.bottom);
14662         }
14663         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14664         height += this.centerBg.getPadding("tb");
14665         return height;
14666     },
14667
14668     // private
14669     syncBodyHeight : function(){
14670         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
14671         var height = this.size.height - this.getHeaderFooterHeight(false);
14672         bd.setHeight(height-bd.getMargins("tb"));
14673         var hh = this.header.getHeight();
14674         var h = this.size.height-hh;
14675         cb.setHeight(h);
14676         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14677         bw.setHeight(h-cb.getPadding("tb"));
14678         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14679         bd.setWidth(bw.getWidth(true));
14680         if(this.tabs){
14681             this.tabs.syncHeight();
14682             if(Roo.isIE){
14683                 this.tabs.el.repaint();
14684             }
14685         }
14686     },
14687
14688     /**
14689      * Restores the previous state of the dialog if Roo.state is configured.
14690      * @return {Roo.BasicDialog} this
14691      */
14692     restoreState : function(){
14693         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14694         if(box && box.width){
14695             this.xy = [box.x, box.y];
14696             this.resizeTo(box.width, box.height);
14697         }
14698         return this;
14699     },
14700
14701     // private
14702     beforeShow : function(){
14703         this.expand();
14704         if(this.fixedcenter){
14705             this.xy = this.el.getCenterXY(true);
14706         }
14707         if(this.modal){
14708             Roo.get(document.body).addClass("x-body-masked");
14709             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14710             this.mask.show();
14711         }
14712         this.constrainXY();
14713     },
14714
14715     // private
14716     animShow : function(){
14717         var b = Roo.get(this.animateTarget).getBox();
14718         this.proxy.setSize(b.width, b.height);
14719         this.proxy.setLocation(b.x, b.y);
14720         this.proxy.show();
14721         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14722                     true, .35, this.showEl.createDelegate(this));
14723     },
14724
14725     /**
14726      * Shows the dialog.
14727      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14728      * @return {Roo.BasicDialog} this
14729      */
14730     show : function(animateTarget){
14731         if (this.fireEvent("beforeshow", this) === false){
14732             return;
14733         }
14734         if(this.syncHeightBeforeShow){
14735             this.syncBodyHeight();
14736         }else if(this.firstShow){
14737             this.firstShow = false;
14738             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14739         }
14740         this.animateTarget = animateTarget || this.animateTarget;
14741         if(!this.el.isVisible()){
14742             this.beforeShow();
14743             if(this.animateTarget && Roo.get(this.animateTarget)){
14744                 this.animShow();
14745             }else{
14746                 this.showEl();
14747             }
14748         }
14749         return this;
14750     },
14751
14752     // private
14753     showEl : function(){
14754         this.proxy.hide();
14755         this.el.setXY(this.xy);
14756         this.el.show();
14757         this.adjustAssets(true);
14758         this.toFront();
14759         this.focus();
14760         // IE peekaboo bug - fix found by Dave Fenwick
14761         if(Roo.isIE){
14762             this.el.repaint();
14763         }
14764         this.fireEvent("show", this);
14765     },
14766
14767     /**
14768      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14769      * dialog itself will receive focus.
14770      */
14771     focus : function(){
14772         if(this.defaultButton){
14773             this.defaultButton.focus();
14774         }else{
14775             this.focusEl.focus();
14776         }
14777     },
14778
14779     // private
14780     constrainXY : function(){
14781         if(this.constraintoviewport !== false){
14782             if(!this.viewSize){
14783                 if(this.container){
14784                     var s = this.container.getSize();
14785                     this.viewSize = [s.width, s.height];
14786                 }else{
14787                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14788                 }
14789             }
14790             var s = Roo.get(this.container||document).getScroll();
14791
14792             var x = this.xy[0], y = this.xy[1];
14793             var w = this.size.width, h = this.size.height;
14794             var vw = this.viewSize[0], vh = this.viewSize[1];
14795             // only move it if it needs it
14796             var moved = false;
14797             // first validate right/bottom
14798             if(x + w > vw+s.left){
14799                 x = vw - w;
14800                 moved = true;
14801             }
14802             if(y + h > vh+s.top){
14803                 y = vh - h;
14804                 moved = true;
14805             }
14806             // then make sure top/left isn't negative
14807             if(x < s.left){
14808                 x = s.left;
14809                 moved = true;
14810             }
14811             if(y < s.top){
14812                 y = s.top;
14813                 moved = true;
14814             }
14815             if(moved){
14816                 // cache xy
14817                 this.xy = [x, y];
14818                 if(this.isVisible()){
14819                     this.el.setLocation(x, y);
14820                     this.adjustAssets();
14821                 }
14822             }
14823         }
14824     },
14825
14826     // private
14827     onDrag : function(){
14828         if(!this.proxyDrag){
14829             this.xy = this.el.getXY();
14830             this.adjustAssets();
14831         }
14832     },
14833
14834     // private
14835     adjustAssets : function(doShow){
14836         var x = this.xy[0], y = this.xy[1];
14837         var w = this.size.width, h = this.size.height;
14838         if(doShow === true){
14839             if(this.shadow){
14840                 this.shadow.show(this.el);
14841             }
14842             if(this.shim){
14843                 this.shim.show();
14844             }
14845         }
14846         if(this.shadow && this.shadow.isVisible()){
14847             this.shadow.show(this.el);
14848         }
14849         if(this.shim && this.shim.isVisible()){
14850             this.shim.setBounds(x, y, w, h);
14851         }
14852     },
14853
14854     // private
14855     adjustViewport : function(w, h){
14856         if(!w || !h){
14857             w = Roo.lib.Dom.getViewWidth();
14858             h = Roo.lib.Dom.getViewHeight();
14859         }
14860         // cache the size
14861         this.viewSize = [w, h];
14862         if(this.modal && this.mask.isVisible()){
14863             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14864             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14865         }
14866         if(this.isVisible()){
14867             this.constrainXY();
14868         }
14869     },
14870
14871     /**
14872      * Destroys this dialog and all its supporting elements (including any tabs, shim,
14873      * shadow, proxy, mask, etc.)  Also removes all event listeners.
14874      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14875      */
14876     destroy : function(removeEl){
14877         if(this.isVisible()){
14878             this.animateTarget = null;
14879             this.hide();
14880         }
14881         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14882         if(this.tabs){
14883             this.tabs.destroy(removeEl);
14884         }
14885         Roo.destroy(
14886              this.shim,
14887              this.proxy,
14888              this.resizer,
14889              this.close,
14890              this.mask
14891         );
14892         if(this.dd){
14893             this.dd.unreg();
14894         }
14895         if(this.buttons){
14896            for(var i = 0, len = this.buttons.length; i < len; i++){
14897                this.buttons[i].destroy();
14898            }
14899         }
14900         this.el.removeAllListeners();
14901         if(removeEl === true){
14902             this.el.update("");
14903             this.el.remove();
14904         }
14905         Roo.DialogManager.unregister(this);
14906     },
14907
14908     // private
14909     startMove : function(){
14910         if(this.proxyDrag){
14911             this.proxy.show();
14912         }
14913         if(this.constraintoviewport !== false){
14914             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
14915         }
14916     },
14917
14918     // private
14919     endMove : function(){
14920         if(!this.proxyDrag){
14921             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
14922         }else{
14923             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
14924             this.proxy.hide();
14925         }
14926         this.refreshSize();
14927         this.adjustAssets();
14928         this.focus();
14929         this.fireEvent("move", this, this.xy[0], this.xy[1]);
14930     },
14931
14932     /**
14933      * Brings this dialog to the front of any other visible dialogs
14934      * @return {Roo.BasicDialog} this
14935      */
14936     toFront : function(){
14937         Roo.DialogManager.bringToFront(this);
14938         return this;
14939     },
14940
14941     /**
14942      * Sends this dialog to the back (under) of any other visible dialogs
14943      * @return {Roo.BasicDialog} this
14944      */
14945     toBack : function(){
14946         Roo.DialogManager.sendToBack(this);
14947         return this;
14948     },
14949
14950     /**
14951      * Centers this dialog in the viewport
14952      * @return {Roo.BasicDialog} this
14953      */
14954     center : function(){
14955         var xy = this.el.getCenterXY(true);
14956         this.moveTo(xy[0], xy[1]);
14957         return this;
14958     },
14959
14960     /**
14961      * Moves the dialog's top-left corner to the specified point
14962      * @param {Number} x
14963      * @param {Number} y
14964      * @return {Roo.BasicDialog} this
14965      */
14966     moveTo : function(x, y){
14967         this.xy = [x,y];
14968         if(this.isVisible()){
14969             this.el.setXY(this.xy);
14970             this.adjustAssets();
14971         }
14972         return this;
14973     },
14974
14975     /**
14976      * Aligns the dialog to the specified element
14977      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14978      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
14979      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14980      * @return {Roo.BasicDialog} this
14981      */
14982     alignTo : function(element, position, offsets){
14983         this.xy = this.el.getAlignToXY(element, position, offsets);
14984         if(this.isVisible()){
14985             this.el.setXY(this.xy);
14986             this.adjustAssets();
14987         }
14988         return this;
14989     },
14990
14991     /**
14992      * Anchors an element to another element and realigns it when the window is resized.
14993      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14994      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
14995      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14996      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
14997      * is a number, it is used as the buffer delay (defaults to 50ms).
14998      * @return {Roo.BasicDialog} this
14999      */
15000     anchorTo : function(el, alignment, offsets, monitorScroll){
15001         var action = function(){
15002             this.alignTo(el, alignment, offsets);
15003         };
15004         Roo.EventManager.onWindowResize(action, this);
15005         var tm = typeof monitorScroll;
15006         if(tm != 'undefined'){
15007             Roo.EventManager.on(window, 'scroll', action, this,
15008                 {buffer: tm == 'number' ? monitorScroll : 50});
15009         }
15010         action.call(this);
15011         return this;
15012     },
15013
15014     /**
15015      * Returns true if the dialog is visible
15016      * @return {Boolean}
15017      */
15018     isVisible : function(){
15019         return this.el.isVisible();
15020     },
15021
15022     // private
15023     animHide : function(callback){
15024         var b = Roo.get(this.animateTarget).getBox();
15025         this.proxy.show();
15026         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
15027         this.el.hide();
15028         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
15029                     this.hideEl.createDelegate(this, [callback]));
15030     },
15031
15032     /**
15033      * Hides the dialog.
15034      * @param {Function} callback (optional) Function to call when the dialog is hidden
15035      * @return {Roo.BasicDialog} this
15036      */
15037     hide : function(callback){
15038         if (this.fireEvent("beforehide", this) === false){
15039             return;
15040         }
15041         if(this.shadow){
15042             this.shadow.hide();
15043         }
15044         if(this.shim) {
15045           this.shim.hide();
15046         }
15047         // sometimes animateTarget seems to get set.. causing problems...
15048         // this just double checks..
15049         if(this.animateTarget && Roo.get(this.animateTarget)) {
15050            this.animHide(callback);
15051         }else{
15052             this.el.hide();
15053             this.hideEl(callback);
15054         }
15055         return this;
15056     },
15057
15058     // private
15059     hideEl : function(callback){
15060         this.proxy.hide();
15061         if(this.modal){
15062             this.mask.hide();
15063             Roo.get(document.body).removeClass("x-body-masked");
15064         }
15065         this.fireEvent("hide", this);
15066         if(typeof callback == "function"){
15067             callback();
15068         }
15069     },
15070
15071     // private
15072     hideAction : function(){
15073         this.setLeft("-10000px");
15074         this.setTop("-10000px");
15075         this.setStyle("visibility", "hidden");
15076     },
15077
15078     // private
15079     refreshSize : function(){
15080         this.size = this.el.getSize();
15081         this.xy = this.el.getXY();
15082         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15083     },
15084
15085     // private
15086     // z-index is managed by the DialogManager and may be overwritten at any time
15087     setZIndex : function(index){
15088         if(this.modal){
15089             this.mask.setStyle("z-index", index);
15090         }
15091         if(this.shim){
15092             this.shim.setStyle("z-index", ++index);
15093         }
15094         if(this.shadow){
15095             this.shadow.setZIndex(++index);
15096         }
15097         this.el.setStyle("z-index", ++index);
15098         if(this.proxy){
15099             this.proxy.setStyle("z-index", ++index);
15100         }
15101         if(this.resizer){
15102             this.resizer.proxy.setStyle("z-index", ++index);
15103         }
15104
15105         this.lastZIndex = index;
15106     },
15107
15108     /**
15109      * Returns the element for this dialog
15110      * @return {Roo.Element} The underlying dialog Element
15111      */
15112     getEl : function(){
15113         return this.el;
15114     }
15115 });
15116
15117 /**
15118  * @class Roo.DialogManager
15119  * Provides global access to BasicDialogs that have been created and
15120  * support for z-indexing (layering) multiple open dialogs.
15121  */
15122 Roo.DialogManager = function(){
15123     var list = {};
15124     var accessList = [];
15125     var front = null;
15126
15127     // private
15128     var sortDialogs = function(d1, d2){
15129         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15130     };
15131
15132     // private
15133     var orderDialogs = function(){
15134         accessList.sort(sortDialogs);
15135         var seed = Roo.DialogManager.zseed;
15136         for(var i = 0, len = accessList.length; i < len; i++){
15137             var dlg = accessList[i];
15138             if(dlg){
15139                 dlg.setZIndex(seed + (i*10));
15140             }
15141         }
15142     };
15143
15144     return {
15145         /**
15146          * The starting z-index for BasicDialogs (defaults to 9000)
15147          * @type Number The z-index value
15148          */
15149         zseed : 9000,
15150
15151         // private
15152         register : function(dlg){
15153             list[dlg.id] = dlg;
15154             accessList.push(dlg);
15155         },
15156
15157         // private
15158         unregister : function(dlg){
15159             delete list[dlg.id];
15160             var i=0;
15161             var len=0;
15162             if(!accessList.indexOf){
15163                 for(  i = 0, len = accessList.length; i < len; i++){
15164                     if(accessList[i] == dlg){
15165                         accessList.splice(i, 1);
15166                         return;
15167                     }
15168                 }
15169             }else{
15170                  i = accessList.indexOf(dlg);
15171                 if(i != -1){
15172                     accessList.splice(i, 1);
15173                 }
15174             }
15175         },
15176
15177         /**
15178          * Gets a registered dialog by id
15179          * @param {String/Object} id The id of the dialog or a dialog
15180          * @return {Roo.BasicDialog} this
15181          */
15182         get : function(id){
15183             return typeof id == "object" ? id : list[id];
15184         },
15185
15186         /**
15187          * Brings the specified dialog to the front
15188          * @param {String/Object} dlg The id of the dialog or a dialog
15189          * @return {Roo.BasicDialog} this
15190          */
15191         bringToFront : function(dlg){
15192             dlg = this.get(dlg);
15193             if(dlg != front){
15194                 front = dlg;
15195                 dlg._lastAccess = new Date().getTime();
15196                 orderDialogs();
15197             }
15198             return dlg;
15199         },
15200
15201         /**
15202          * Sends the specified dialog to the back
15203          * @param {String/Object} dlg The id of the dialog or a dialog
15204          * @return {Roo.BasicDialog} this
15205          */
15206         sendToBack : function(dlg){
15207             dlg = this.get(dlg);
15208             dlg._lastAccess = -(new Date().getTime());
15209             orderDialogs();
15210             return dlg;
15211         },
15212
15213         /**
15214          * Hides all dialogs
15215          */
15216         hideAll : function(){
15217             for(var id in list){
15218                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15219                     list[id].hide();
15220                 }
15221             }
15222         }
15223     };
15224 }();
15225
15226 /**
15227  * @class Roo.LayoutDialog
15228  * @extends Roo.BasicDialog
15229  * Dialog which provides adjustments for working with a layout in a Dialog.
15230  * Add your necessary layout config options to the dialog's config.<br>
15231  * Example usage (including a nested layout):
15232  * <pre><code>
15233 if(!dialog){
15234     dialog = new Roo.LayoutDialog("download-dlg", {
15235         modal: true,
15236         width:600,
15237         height:450,
15238         shadow:true,
15239         minWidth:500,
15240         minHeight:350,
15241         autoTabs:true,
15242         proxyDrag:true,
15243         // layout config merges with the dialog config
15244         center:{
15245             tabPosition: "top",
15246             alwaysShowTabs: true
15247         }
15248     });
15249     dialog.addKeyListener(27, dialog.hide, dialog);
15250     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15251     dialog.addButton("Build It!", this.getDownload, this);
15252
15253     // we can even add nested layouts
15254     var innerLayout = new Roo.BorderLayout("dl-inner", {
15255         east: {
15256             initialSize: 200,
15257             autoScroll:true,
15258             split:true
15259         },
15260         center: {
15261             autoScroll:true
15262         }
15263     });
15264     innerLayout.beginUpdate();
15265     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15266     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15267     innerLayout.endUpdate(true);
15268
15269     var layout = dialog.getLayout();
15270     layout.beginUpdate();
15271     layout.add("center", new Roo.ContentPanel("standard-panel",
15272                         {title: "Download the Source", fitToFrame:true}));
15273     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15274                {title: "Build your own roo.js"}));
15275     layout.getRegion("center").showPanel(sp);
15276     layout.endUpdate();
15277 }
15278 </code></pre>
15279     * @constructor
15280     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15281     * @param {Object} config configuration options
15282   */
15283 Roo.LayoutDialog = function(el, cfg){
15284     
15285     var config=  cfg;
15286     if (typeof(cfg) == 'undefined') {
15287         config = Roo.apply({}, el);
15288         // not sure why we use documentElement here.. - it should always be body.
15289         // IE7 borks horribly if we use documentElement.
15290         // webkit also does not like documentElement - it creates a body element...
15291         el = Roo.get( document.body || document.documentElement ).createChild();
15292         //config.autoCreate = true;
15293     }
15294     
15295     
15296     config.autoTabs = false;
15297     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15298     this.body.setStyle({overflow:"hidden", position:"relative"});
15299     this.layout = new Roo.BorderLayout(this.body.dom, config);
15300     this.layout.monitorWindowResize = false;
15301     this.el.addClass("x-dlg-auto-layout");
15302     // fix case when center region overwrites center function
15303     this.center = Roo.BasicDialog.prototype.center;
15304     this.on("show", this.layout.layout, this.layout, true);
15305     if (config.items) {
15306         var xitems = config.items;
15307         delete config.items;
15308         Roo.each(xitems, this.addxtype, this);
15309     }
15310     
15311     
15312 };
15313 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15314     /**
15315      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15316      * @deprecated
15317      */
15318     endUpdate : function(){
15319         this.layout.endUpdate();
15320     },
15321
15322     /**
15323      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15324      *  @deprecated
15325      */
15326     beginUpdate : function(){
15327         this.layout.beginUpdate();
15328     },
15329
15330     /**
15331      * Get the BorderLayout for this dialog
15332      * @return {Roo.BorderLayout}
15333      */
15334     getLayout : function(){
15335         return this.layout;
15336     },
15337
15338     showEl : function(){
15339         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15340         if(Roo.isIE7){
15341             this.layout.layout();
15342         }
15343     },
15344
15345     // private
15346     // Use the syncHeightBeforeShow config option to control this automatically
15347     syncBodyHeight : function(){
15348         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15349         if(this.layout){this.layout.layout();}
15350     },
15351     
15352       /**
15353      * Add an xtype element (actually adds to the layout.)
15354      * @return {Object} xdata xtype object data.
15355      */
15356     
15357     addxtype : function(c) {
15358         return this.layout.addxtype(c);
15359     }
15360 });/*
15361  * Based on:
15362  * Ext JS Library 1.1.1
15363  * Copyright(c) 2006-2007, Ext JS, LLC.
15364  *
15365  * Originally Released Under LGPL - original licence link has changed is not relivant.
15366  *
15367  * Fork - LGPL
15368  * <script type="text/javascript">
15369  */
15370  
15371 /**
15372  * @class Roo.MessageBox
15373  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15374  * Example usage:
15375  *<pre><code>
15376 // Basic alert:
15377 Roo.Msg.alert('Status', 'Changes saved successfully.');
15378
15379 // Prompt for user data:
15380 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15381     if (btn == 'ok'){
15382         // process text value...
15383     }
15384 });
15385
15386 // Show a dialog using config options:
15387 Roo.Msg.show({
15388    title:'Save Changes?',
15389    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15390    buttons: Roo.Msg.YESNOCANCEL,
15391    fn: processResult,
15392    animEl: 'elId'
15393 });
15394 </code></pre>
15395  * @singleton
15396  */
15397 Roo.MessageBox = function(){
15398     var dlg, opt, mask, waitTimer;
15399     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15400     var buttons, activeTextEl, bwidth;
15401
15402     // private
15403     var handleButton = function(button){
15404         dlg.hide();
15405         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15406     };
15407
15408     // private
15409     var handleHide = function(){
15410         if(opt && opt.cls){
15411             dlg.el.removeClass(opt.cls);
15412         }
15413         if(waitTimer){
15414             Roo.TaskMgr.stop(waitTimer);
15415             waitTimer = null;
15416         }
15417     };
15418
15419     // private
15420     var updateButtons = function(b){
15421         var width = 0;
15422         if(!b){
15423             buttons["ok"].hide();
15424             buttons["cancel"].hide();
15425             buttons["yes"].hide();
15426             buttons["no"].hide();
15427             dlg.footer.dom.style.display = 'none';
15428             return width;
15429         }
15430         dlg.footer.dom.style.display = '';
15431         for(var k in buttons){
15432             if(typeof buttons[k] != "function"){
15433                 if(b[k]){
15434                     buttons[k].show();
15435                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15436                     width += buttons[k].el.getWidth()+15;
15437                 }else{
15438                     buttons[k].hide();
15439                 }
15440             }
15441         }
15442         return width;
15443     };
15444
15445     // private
15446     var handleEsc = function(d, k, e){
15447         if(opt && opt.closable !== false){
15448             dlg.hide();
15449         }
15450         if(e){
15451             e.stopEvent();
15452         }
15453     };
15454
15455     return {
15456         /**
15457          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15458          * @return {Roo.BasicDialog} The BasicDialog element
15459          */
15460         getDialog : function(){
15461            if(!dlg){
15462                 dlg = new Roo.BasicDialog("x-msg-box", {
15463                     autoCreate : true,
15464                     shadow: true,
15465                     draggable: true,
15466                     resizable:false,
15467                     constraintoviewport:false,
15468                     fixedcenter:true,
15469                     collapsible : false,
15470                     shim:true,
15471                     modal: true,
15472                     width:400, height:100,
15473                     buttonAlign:"center",
15474                     closeClick : function(){
15475                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15476                             handleButton("no");
15477                         }else{
15478                             handleButton("cancel");
15479                         }
15480                     }
15481                 });
15482                 dlg.on("hide", handleHide);
15483                 mask = dlg.mask;
15484                 dlg.addKeyListener(27, handleEsc);
15485                 buttons = {};
15486                 var bt = this.buttonText;
15487                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15488                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15489                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15490                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15491                 bodyEl = dlg.body.createChild({
15492
15493                     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>'
15494                 });
15495                 msgEl = bodyEl.dom.firstChild;
15496                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15497                 textboxEl.enableDisplayMode();
15498                 textboxEl.addKeyListener([10,13], function(){
15499                     if(dlg.isVisible() && opt && opt.buttons){
15500                         if(opt.buttons.ok){
15501                             handleButton("ok");
15502                         }else if(opt.buttons.yes){
15503                             handleButton("yes");
15504                         }
15505                     }
15506                 });
15507                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15508                 textareaEl.enableDisplayMode();
15509                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15510                 progressEl.enableDisplayMode();
15511                 var pf = progressEl.dom.firstChild;
15512                 if (pf) {
15513                     pp = Roo.get(pf.firstChild);
15514                     pp.setHeight(pf.offsetHeight);
15515                 }
15516                 
15517             }
15518             return dlg;
15519         },
15520
15521         /**
15522          * Updates the message box body text
15523          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15524          * the XHTML-compliant non-breaking space character '&amp;#160;')
15525          * @return {Roo.MessageBox} This message box
15526          */
15527         updateText : function(text){
15528             if(!dlg.isVisible() && !opt.width){
15529                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15530             }
15531             msgEl.innerHTML = text || '&#160;';
15532             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
15533                         Math.max(opt.minWidth || this.minWidth, bwidth));
15534             if(opt.prompt){
15535                 activeTextEl.setWidth(w);
15536             }
15537             if(dlg.isVisible()){
15538                 dlg.fixedcenter = false;
15539             }
15540             dlg.setContentSize(w, bodyEl.getHeight());
15541             if(dlg.isVisible()){
15542                 dlg.fixedcenter = true;
15543             }
15544             return this;
15545         },
15546
15547         /**
15548          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15549          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15550          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15551          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15552          * @return {Roo.MessageBox} This message box
15553          */
15554         updateProgress : function(value, text){
15555             if(text){
15556                 this.updateText(text);
15557             }
15558             if (pp) { // weird bug on my firefox - for some reason this is not defined
15559                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15560             }
15561             return this;
15562         },        
15563
15564         /**
15565          * Returns true if the message box is currently displayed
15566          * @return {Boolean} True if the message box is visible, else false
15567          */
15568         isVisible : function(){
15569             return dlg && dlg.isVisible();  
15570         },
15571
15572         /**
15573          * Hides the message box if it is displayed
15574          */
15575         hide : function(){
15576             if(this.isVisible()){
15577                 dlg.hide();
15578             }  
15579         },
15580
15581         /**
15582          * Displays a new message box, or reinitializes an existing message box, based on the config options
15583          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15584          * The following config object properties are supported:
15585          * <pre>
15586 Property    Type             Description
15587 ----------  ---------------  ------------------------------------------------------------------------------------
15588 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15589                                    closes (defaults to undefined)
15590 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15591                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15592 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15593                                    progress and wait dialogs will ignore this property and always hide the
15594                                    close button as they can only be closed programmatically.
15595 cls               String           A custom CSS class to apply to the message box element
15596 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15597                                    displayed (defaults to 75)
15598 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15599                                    function will be btn (the name of the button that was clicked, if applicable,
15600                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15601                                    Progress and wait dialogs will ignore this option since they do not respond to
15602                                    user actions and can only be closed programmatically, so any required function
15603                                    should be called by the same code after it closes the dialog.
15604 icon              String           A CSS class that provides a background image to be used as an icon for
15605                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15606 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15607 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15608 modal             Boolean          False to allow user interaction with the page while the message box is
15609                                    displayed (defaults to true)
15610 msg               String           A string that will replace the existing message box body text (defaults
15611                                    to the XHTML-compliant non-breaking space character '&#160;')
15612 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15613 progress          Boolean          True to display a progress bar (defaults to false)
15614 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15615 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15616 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15617 title             String           The title text
15618 value             String           The string value to set into the active textbox element if displayed
15619 wait              Boolean          True to display a progress bar (defaults to false)
15620 width             Number           The width of the dialog in pixels
15621 </pre>
15622          *
15623          * Example usage:
15624          * <pre><code>
15625 Roo.Msg.show({
15626    title: 'Address',
15627    msg: 'Please enter your address:',
15628    width: 300,
15629    buttons: Roo.MessageBox.OKCANCEL,
15630    multiline: true,
15631    fn: saveAddress,
15632    animEl: 'addAddressBtn'
15633 });
15634 </code></pre>
15635          * @param {Object} config Configuration options
15636          * @return {Roo.MessageBox} This message box
15637          */
15638         show : function(options){
15639             if(this.isVisible()){
15640                 this.hide();
15641             }
15642             var d = this.getDialog();
15643             opt = options;
15644             d.setTitle(opt.title || "&#160;");
15645             d.close.setDisplayed(opt.closable !== false);
15646             activeTextEl = textboxEl;
15647             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15648             if(opt.prompt){
15649                 if(opt.multiline){
15650                     textboxEl.hide();
15651                     textareaEl.show();
15652                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15653                         opt.multiline : this.defaultTextHeight);
15654                     activeTextEl = textareaEl;
15655                 }else{
15656                     textboxEl.show();
15657                     textareaEl.hide();
15658                 }
15659             }else{
15660                 textboxEl.hide();
15661                 textareaEl.hide();
15662             }
15663             progressEl.setDisplayed(opt.progress === true);
15664             this.updateProgress(0);
15665             activeTextEl.dom.value = opt.value || "";
15666             if(opt.prompt){
15667                 dlg.setDefaultButton(activeTextEl);
15668             }else{
15669                 var bs = opt.buttons;
15670                 var db = null;
15671                 if(bs && bs.ok){
15672                     db = buttons["ok"];
15673                 }else if(bs && bs.yes){
15674                     db = buttons["yes"];
15675                 }
15676                 dlg.setDefaultButton(db);
15677             }
15678             bwidth = updateButtons(opt.buttons);
15679             this.updateText(opt.msg);
15680             if(opt.cls){
15681                 d.el.addClass(opt.cls);
15682             }
15683             d.proxyDrag = opt.proxyDrag === true;
15684             d.modal = opt.modal !== false;
15685             d.mask = opt.modal !== false ? mask : false;
15686             if(!d.isVisible()){
15687                 // force it to the end of the z-index stack so it gets a cursor in FF
15688                 document.body.appendChild(dlg.el.dom);
15689                 d.animateTarget = null;
15690                 d.show(options.animEl);
15691             }
15692             return this;
15693         },
15694
15695         /**
15696          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15697          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15698          * and closing the message box when the process is complete.
15699          * @param {String} title The title bar text
15700          * @param {String} msg The message box body text
15701          * @return {Roo.MessageBox} This message box
15702          */
15703         progress : function(title, msg){
15704             this.show({
15705                 title : title,
15706                 msg : msg,
15707                 buttons: false,
15708                 progress:true,
15709                 closable:false,
15710                 minWidth: this.minProgressWidth,
15711                 modal : true
15712             });
15713             return this;
15714         },
15715
15716         /**
15717          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15718          * If a callback function is passed it will be called after the user clicks the button, and the
15719          * id of the button that was clicked will be passed as the only parameter to the callback
15720          * (could also be the top-right close button).
15721          * @param {String} title The title bar text
15722          * @param {String} msg The message box body text
15723          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15724          * @param {Object} scope (optional) The scope of the callback function
15725          * @return {Roo.MessageBox} This message box
15726          */
15727         alert : function(title, msg, fn, scope){
15728             this.show({
15729                 title : title,
15730                 msg : msg,
15731                 buttons: this.OK,
15732                 fn: fn,
15733                 scope : scope,
15734                 modal : true
15735             });
15736             return this;
15737         },
15738
15739         /**
15740          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15741          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15742          * You are responsible for closing the message box when the process is complete.
15743          * @param {String} msg The message box body text
15744          * @param {String} title (optional) The title bar text
15745          * @return {Roo.MessageBox} This message box
15746          */
15747         wait : function(msg, title){
15748             this.show({
15749                 title : title,
15750                 msg : msg,
15751                 buttons: false,
15752                 closable:false,
15753                 progress:true,
15754                 modal:true,
15755                 width:300,
15756                 wait:true
15757             });
15758             waitTimer = Roo.TaskMgr.start({
15759                 run: function(i){
15760                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15761                 },
15762                 interval: 1000
15763             });
15764             return this;
15765         },
15766
15767         /**
15768          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15769          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15770          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15771          * @param {String} title The title bar text
15772          * @param {String} msg The message box body text
15773          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15774          * @param {Object} scope (optional) The scope of the callback function
15775          * @return {Roo.MessageBox} This message box
15776          */
15777         confirm : function(title, msg, fn, scope){
15778             this.show({
15779                 title : title,
15780                 msg : msg,
15781                 buttons: this.YESNO,
15782                 fn: fn,
15783                 scope : scope,
15784                 modal : true
15785             });
15786             return this;
15787         },
15788
15789         /**
15790          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15791          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15792          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15793          * (could also be the top-right close button) and the text that was entered will be passed as the two
15794          * parameters to the callback.
15795          * @param {String} title The title bar text
15796          * @param {String} msg The message box body text
15797          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15798          * @param {Object} scope (optional) The scope of the callback function
15799          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15800          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15801          * @return {Roo.MessageBox} This message box
15802          */
15803         prompt : function(title, msg, fn, scope, multiline){
15804             this.show({
15805                 title : title,
15806                 msg : msg,
15807                 buttons: this.OKCANCEL,
15808                 fn: fn,
15809                 minWidth:250,
15810                 scope : scope,
15811                 prompt:true,
15812                 multiline: multiline,
15813                 modal : true
15814             });
15815             return this;
15816         },
15817
15818         /**
15819          * Button config that displays a single OK button
15820          * @type Object
15821          */
15822         OK : {ok:true},
15823         /**
15824          * Button config that displays Yes and No buttons
15825          * @type Object
15826          */
15827         YESNO : {yes:true, no:true},
15828         /**
15829          * Button config that displays OK and Cancel buttons
15830          * @type Object
15831          */
15832         OKCANCEL : {ok:true, cancel:true},
15833         /**
15834          * Button config that displays Yes, No and Cancel buttons
15835          * @type Object
15836          */
15837         YESNOCANCEL : {yes:true, no:true, cancel:true},
15838
15839         /**
15840          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15841          * @type Number
15842          */
15843         defaultTextHeight : 75,
15844         /**
15845          * The maximum width in pixels of the message box (defaults to 600)
15846          * @type Number
15847          */
15848         maxWidth : 600,
15849         /**
15850          * The minimum width in pixels of the message box (defaults to 100)
15851          * @type Number
15852          */
15853         minWidth : 100,
15854         /**
15855          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
15856          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
15857          * @type Number
15858          */
15859         minProgressWidth : 250,
15860         /**
15861          * An object containing the default button text strings that can be overriden for localized language support.
15862          * Supported properties are: ok, cancel, yes and no.
15863          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
15864          * @type Object
15865          */
15866         buttonText : {
15867             ok : "OK",
15868             cancel : "Cancel",
15869             yes : "Yes",
15870             no : "No"
15871         }
15872     };
15873 }();
15874
15875 /**
15876  * Shorthand for {@link Roo.MessageBox}
15877  */
15878 Roo.Msg = Roo.MessageBox;/*
15879  * Based on:
15880  * Ext JS Library 1.1.1
15881  * Copyright(c) 2006-2007, Ext JS, LLC.
15882  *
15883  * Originally Released Under LGPL - original licence link has changed is not relivant.
15884  *
15885  * Fork - LGPL
15886  * <script type="text/javascript">
15887  */
15888 /**
15889  * @class Roo.QuickTips
15890  * Provides attractive and customizable tooltips for any element.
15891  * @singleton
15892  */
15893 Roo.QuickTips = function(){
15894     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
15895     var ce, bd, xy, dd;
15896     var visible = false, disabled = true, inited = false;
15897     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
15898     
15899     var onOver = function(e){
15900         if(disabled){
15901             return;
15902         }
15903         var t = e.getTarget();
15904         if(!t || t.nodeType !== 1 || t == document || t == document.body){
15905             return;
15906         }
15907         if(ce && t == ce.el){
15908             clearTimeout(hideProc);
15909             return;
15910         }
15911         if(t && tagEls[t.id]){
15912             tagEls[t.id].el = t;
15913             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
15914             return;
15915         }
15916         var ttp, et = Roo.fly(t);
15917         var ns = cfg.namespace;
15918         if(tm.interceptTitles && t.title){
15919             ttp = t.title;
15920             t.qtip = ttp;
15921             t.removeAttribute("title");
15922             e.preventDefault();
15923         }else{
15924             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
15925         }
15926         if(ttp){
15927             showProc = show.defer(tm.showDelay, tm, [{
15928                 el: t, 
15929                 text: ttp, 
15930                 width: et.getAttributeNS(ns, cfg.width),
15931                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
15932                 title: et.getAttributeNS(ns, cfg.title),
15933                     cls: et.getAttributeNS(ns, cfg.cls)
15934             }]);
15935         }
15936     };
15937     
15938     var onOut = function(e){
15939         clearTimeout(showProc);
15940         var t = e.getTarget();
15941         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
15942             hideProc = setTimeout(hide, tm.hideDelay);
15943         }
15944     };
15945     
15946     var onMove = function(e){
15947         if(disabled){
15948             return;
15949         }
15950         xy = e.getXY();
15951         xy[1] += 18;
15952         if(tm.trackMouse && ce){
15953             el.setXY(xy);
15954         }
15955     };
15956     
15957     var onDown = function(e){
15958         clearTimeout(showProc);
15959         clearTimeout(hideProc);
15960         if(!e.within(el)){
15961             if(tm.hideOnClick){
15962                 hide();
15963                 tm.disable();
15964                 tm.enable.defer(100, tm);
15965             }
15966         }
15967     };
15968     
15969     var getPad = function(){
15970         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
15971     };
15972
15973     var show = function(o){
15974         if(disabled){
15975             return;
15976         }
15977         clearTimeout(dismissProc);
15978         ce = o;
15979         if(removeCls){ // in case manually hidden
15980             el.removeClass(removeCls);
15981             removeCls = null;
15982         }
15983         if(ce.cls){
15984             el.addClass(ce.cls);
15985             removeCls = ce.cls;
15986         }
15987         if(ce.title){
15988             tipTitle.update(ce.title);
15989             tipTitle.show();
15990         }else{
15991             tipTitle.update('');
15992             tipTitle.hide();
15993         }
15994         el.dom.style.width  = tm.maxWidth+'px';
15995         //tipBody.dom.style.width = '';
15996         tipBodyText.update(o.text);
15997         var p = getPad(), w = ce.width;
15998         if(!w){
15999             var td = tipBodyText.dom;
16000             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
16001             if(aw > tm.maxWidth){
16002                 w = tm.maxWidth;
16003             }else if(aw < tm.minWidth){
16004                 w = tm.minWidth;
16005             }else{
16006                 w = aw;
16007             }
16008         }
16009         //tipBody.setWidth(w);
16010         el.setWidth(parseInt(w, 10) + p);
16011         if(ce.autoHide === false){
16012             close.setDisplayed(true);
16013             if(dd){
16014                 dd.unlock();
16015             }
16016         }else{
16017             close.setDisplayed(false);
16018             if(dd){
16019                 dd.lock();
16020             }
16021         }
16022         if(xy){
16023             el.avoidY = xy[1]-18;
16024             el.setXY(xy);
16025         }
16026         if(tm.animate){
16027             el.setOpacity(.1);
16028             el.setStyle("visibility", "visible");
16029             el.fadeIn({callback: afterShow});
16030         }else{
16031             afterShow();
16032         }
16033     };
16034     
16035     var afterShow = function(){
16036         if(ce){
16037             el.show();
16038             esc.enable();
16039             if(tm.autoDismiss && ce.autoHide !== false){
16040                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16041             }
16042         }
16043     };
16044     
16045     var hide = function(noanim){
16046         clearTimeout(dismissProc);
16047         clearTimeout(hideProc);
16048         ce = null;
16049         if(el.isVisible()){
16050             esc.disable();
16051             if(noanim !== true && tm.animate){
16052                 el.fadeOut({callback: afterHide});
16053             }else{
16054                 afterHide();
16055             } 
16056         }
16057     };
16058     
16059     var afterHide = function(){
16060         el.hide();
16061         if(removeCls){
16062             el.removeClass(removeCls);
16063             removeCls = null;
16064         }
16065     };
16066     
16067     return {
16068         /**
16069         * @cfg {Number} minWidth
16070         * The minimum width of the quick tip (defaults to 40)
16071         */
16072        minWidth : 40,
16073         /**
16074         * @cfg {Number} maxWidth
16075         * The maximum width of the quick tip (defaults to 300)
16076         */
16077        maxWidth : 300,
16078         /**
16079         * @cfg {Boolean} interceptTitles
16080         * True to automatically use the element's DOM title value if available (defaults to false)
16081         */
16082        interceptTitles : false,
16083         /**
16084         * @cfg {Boolean} trackMouse
16085         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16086         */
16087        trackMouse : false,
16088         /**
16089         * @cfg {Boolean} hideOnClick
16090         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16091         */
16092        hideOnClick : true,
16093         /**
16094         * @cfg {Number} showDelay
16095         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16096         */
16097        showDelay : 500,
16098         /**
16099         * @cfg {Number} hideDelay
16100         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16101         */
16102        hideDelay : 200,
16103         /**
16104         * @cfg {Boolean} autoHide
16105         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16106         * Used in conjunction with hideDelay.
16107         */
16108        autoHide : true,
16109         /**
16110         * @cfg {Boolean}
16111         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16112         * (defaults to true).  Used in conjunction with autoDismissDelay.
16113         */
16114        autoDismiss : true,
16115         /**
16116         * @cfg {Number}
16117         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16118         */
16119        autoDismissDelay : 5000,
16120        /**
16121         * @cfg {Boolean} animate
16122         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16123         */
16124        animate : false,
16125
16126        /**
16127         * @cfg {String} title
16128         * Title text to display (defaults to '').  This can be any valid HTML markup.
16129         */
16130         title: '',
16131        /**
16132         * @cfg {String} text
16133         * Body text to display (defaults to '').  This can be any valid HTML markup.
16134         */
16135         text : '',
16136        /**
16137         * @cfg {String} cls
16138         * A CSS class to apply to the base quick tip element (defaults to '').
16139         */
16140         cls : '',
16141        /**
16142         * @cfg {Number} width
16143         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16144         * minWidth or maxWidth.
16145         */
16146         width : null,
16147
16148     /**
16149      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16150      * or display QuickTips in a page.
16151      */
16152        init : function(){
16153           tm = Roo.QuickTips;
16154           cfg = tm.tagConfig;
16155           if(!inited){
16156               if(!Roo.isReady){ // allow calling of init() before onReady
16157                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16158                   return;
16159               }
16160               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16161               el.fxDefaults = {stopFx: true};
16162               // maximum custom styling
16163               //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>');
16164               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>');              
16165               tipTitle = el.child('h3');
16166               tipTitle.enableDisplayMode("block");
16167               tipBody = el.child('div.x-tip-bd');
16168               tipBodyText = el.child('div.x-tip-bd-inner');
16169               //bdLeft = el.child('div.x-tip-bd-left');
16170               //bdRight = el.child('div.x-tip-bd-right');
16171               close = el.child('div.x-tip-close');
16172               close.enableDisplayMode("block");
16173               close.on("click", hide);
16174               var d = Roo.get(document);
16175               d.on("mousedown", onDown);
16176               d.on("mouseover", onOver);
16177               d.on("mouseout", onOut);
16178               d.on("mousemove", onMove);
16179               esc = d.addKeyListener(27, hide);
16180               esc.disable();
16181               if(Roo.dd.DD){
16182                   dd = el.initDD("default", null, {
16183                       onDrag : function(){
16184                           el.sync();  
16185                       }
16186                   });
16187                   dd.setHandleElId(tipTitle.id);
16188                   dd.lock();
16189               }
16190               inited = true;
16191           }
16192           this.enable(); 
16193        },
16194
16195     /**
16196      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16197      * are supported:
16198      * <pre>
16199 Property    Type                   Description
16200 ----------  ---------------------  ------------------------------------------------------------------------
16201 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16202      * </ul>
16203      * @param {Object} config The config object
16204      */
16205        register : function(config){
16206            var cs = config instanceof Array ? config : arguments;
16207            for(var i = 0, len = cs.length; i < len; i++) {
16208                var c = cs[i];
16209                var target = c.target;
16210                if(target){
16211                    if(target instanceof Array){
16212                        for(var j = 0, jlen = target.length; j < jlen; j++){
16213                            tagEls[target[j]] = c;
16214                        }
16215                    }else{
16216                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16217                    }
16218                }
16219            }
16220        },
16221
16222     /**
16223      * Removes this quick tip from its element and destroys it.
16224      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16225      */
16226        unregister : function(el){
16227            delete tagEls[Roo.id(el)];
16228        },
16229
16230     /**
16231      * Enable this quick tip.
16232      */
16233        enable : function(){
16234            if(inited && disabled){
16235                locks.pop();
16236                if(locks.length < 1){
16237                    disabled = false;
16238                }
16239            }
16240        },
16241
16242     /**
16243      * Disable this quick tip.
16244      */
16245        disable : function(){
16246           disabled = true;
16247           clearTimeout(showProc);
16248           clearTimeout(hideProc);
16249           clearTimeout(dismissProc);
16250           if(ce){
16251               hide(true);
16252           }
16253           locks.push(1);
16254        },
16255
16256     /**
16257      * Returns true if the quick tip is enabled, else false.
16258      */
16259        isEnabled : function(){
16260             return !disabled;
16261        },
16262
16263         // private
16264        tagConfig : {
16265            namespace : "ext",
16266            attribute : "qtip",
16267            width : "width",
16268            target : "target",
16269            title : "qtitle",
16270            hide : "hide",
16271            cls : "qclass"
16272        }
16273    };
16274 }();
16275
16276 // backwards compat
16277 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16278  * Based on:
16279  * Ext JS Library 1.1.1
16280  * Copyright(c) 2006-2007, Ext JS, LLC.
16281  *
16282  * Originally Released Under LGPL - original licence link has changed is not relivant.
16283  *
16284  * Fork - LGPL
16285  * <script type="text/javascript">
16286  */
16287  
16288
16289 /**
16290  * @class Roo.tree.TreePanel
16291  * @extends Roo.data.Tree
16292
16293  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16294  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16295  * @cfg {Boolean} enableDD true to enable drag and drop
16296  * @cfg {Boolean} enableDrag true to enable just drag
16297  * @cfg {Boolean} enableDrop true to enable just drop
16298  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16299  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16300  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16301  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16302  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16303  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16304  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16305  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16306  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16307  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16308  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16309  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16310  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16311  * @cfg {Function} renderer 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>
16312  * @cfg {Function} rendererTip 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>
16313  * 
16314  * @constructor
16315  * @param {String/HTMLElement/Element} el The container element
16316  * @param {Object} config
16317  */
16318 Roo.tree.TreePanel = function(el, config){
16319     var root = false;
16320     var loader = false;
16321     if (config.root) {
16322         root = config.root;
16323         delete config.root;
16324     }
16325     if (config.loader) {
16326         loader = config.loader;
16327         delete config.loader;
16328     }
16329     
16330     Roo.apply(this, config);
16331     Roo.tree.TreePanel.superclass.constructor.call(this);
16332     this.el = Roo.get(el);
16333     this.el.addClass('x-tree');
16334     //console.log(root);
16335     if (root) {
16336         this.setRootNode( Roo.factory(root, Roo.tree));
16337     }
16338     if (loader) {
16339         this.loader = Roo.factory(loader, Roo.tree);
16340     }
16341    /**
16342     * Read-only. The id of the container element becomes this TreePanel's id.
16343     */
16344    this.id = this.el.id;
16345    this.addEvents({
16346         /**
16347         * @event beforeload
16348         * Fires before a node is loaded, return false to cancel
16349         * @param {Node} node The node being loaded
16350         */
16351         "beforeload" : true,
16352         /**
16353         * @event load
16354         * Fires when a node is loaded
16355         * @param {Node} node The node that was loaded
16356         */
16357         "load" : true,
16358         /**
16359         * @event textchange
16360         * Fires when the text for a node is changed
16361         * @param {Node} node The node
16362         * @param {String} text The new text
16363         * @param {String} oldText The old text
16364         */
16365         "textchange" : true,
16366         /**
16367         * @event beforeexpand
16368         * Fires before a node is expanded, return false to cancel.
16369         * @param {Node} node The node
16370         * @param {Boolean} deep
16371         * @param {Boolean} anim
16372         */
16373         "beforeexpand" : true,
16374         /**
16375         * @event beforecollapse
16376         * Fires before a node is collapsed, return false to cancel.
16377         * @param {Node} node The node
16378         * @param {Boolean} deep
16379         * @param {Boolean} anim
16380         */
16381         "beforecollapse" : true,
16382         /**
16383         * @event expand
16384         * Fires when a node is expanded
16385         * @param {Node} node The node
16386         */
16387         "expand" : true,
16388         /**
16389         * @event disabledchange
16390         * Fires when the disabled status of a node changes
16391         * @param {Node} node The node
16392         * @param {Boolean} disabled
16393         */
16394         "disabledchange" : true,
16395         /**
16396         * @event collapse
16397         * Fires when a node is collapsed
16398         * @param {Node} node The node
16399         */
16400         "collapse" : true,
16401         /**
16402         * @event beforeclick
16403         * Fires before click processing on a node. Return false to cancel the default action.
16404         * @param {Node} node The node
16405         * @param {Roo.EventObject} e The event object
16406         */
16407         "beforeclick":true,
16408         /**
16409         * @event checkchange
16410         * Fires when a node with a checkbox's checked property changes
16411         * @param {Node} this This node
16412         * @param {Boolean} checked
16413         */
16414         "checkchange":true,
16415         /**
16416         * @event click
16417         * Fires when a node is clicked
16418         * @param {Node} node The node
16419         * @param {Roo.EventObject} e The event object
16420         */
16421         "click":true,
16422         /**
16423         * @event dblclick
16424         * Fires when a node is double clicked
16425         * @param {Node} node The node
16426         * @param {Roo.EventObject} e The event object
16427         */
16428         "dblclick":true,
16429         /**
16430         * @event contextmenu
16431         * Fires when a node is right clicked
16432         * @param {Node} node The node
16433         * @param {Roo.EventObject} e The event object
16434         */
16435         "contextmenu":true,
16436         /**
16437         * @event beforechildrenrendered
16438         * Fires right before the child nodes for a node are rendered
16439         * @param {Node} node The node
16440         */
16441         "beforechildrenrendered":true,
16442        /**
16443              * @event startdrag
16444              * Fires when a node starts being dragged
16445              * @param {Roo.tree.TreePanel} this
16446              * @param {Roo.tree.TreeNode} node
16447              * @param {event} e The raw browser event
16448              */ 
16449             "startdrag" : true,
16450             /**
16451              * @event enddrag
16452              * Fires when a drag operation is complete
16453              * @param {Roo.tree.TreePanel} this
16454              * @param {Roo.tree.TreeNode} node
16455              * @param {event} e The raw browser event
16456              */
16457             "enddrag" : true,
16458             /**
16459              * @event dragdrop
16460              * Fires when a dragged node is dropped on a valid DD target
16461              * @param {Roo.tree.TreePanel} this
16462              * @param {Roo.tree.TreeNode} node
16463              * @param {DD} dd The dd it was dropped on
16464              * @param {event} e The raw browser event
16465              */
16466             "dragdrop" : true,
16467             /**
16468              * @event beforenodedrop
16469              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16470              * passed to handlers has the following properties:<br />
16471              * <ul style="padding:5px;padding-left:16px;">
16472              * <li>tree - The TreePanel</li>
16473              * <li>target - The node being targeted for the drop</li>
16474              * <li>data - The drag data from the drag source</li>
16475              * <li>point - The point of the drop - append, above or below</li>
16476              * <li>source - The drag source</li>
16477              * <li>rawEvent - Raw mouse event</li>
16478              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16479              * to be inserted by setting them on this object.</li>
16480              * <li>cancel - Set this to true to cancel the drop.</li>
16481              * </ul>
16482              * @param {Object} dropEvent
16483              */
16484             "beforenodedrop" : true,
16485             /**
16486              * @event nodedrop
16487              * Fires after a DD object is dropped on a node in this tree. The dropEvent
16488              * passed to handlers has the following properties:<br />
16489              * <ul style="padding:5px;padding-left:16px;">
16490              * <li>tree - The TreePanel</li>
16491              * <li>target - The node being targeted for the drop</li>
16492              * <li>data - The drag data from the drag source</li>
16493              * <li>point - The point of the drop - append, above or below</li>
16494              * <li>source - The drag source</li>
16495              * <li>rawEvent - Raw mouse event</li>
16496              * <li>dropNode - Dropped node(s).</li>
16497              * </ul>
16498              * @param {Object} dropEvent
16499              */
16500             "nodedrop" : true,
16501              /**
16502              * @event nodedragover
16503              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16504              * passed to handlers has the following properties:<br />
16505              * <ul style="padding:5px;padding-left:16px;">
16506              * <li>tree - The TreePanel</li>
16507              * <li>target - The node being targeted for the drop</li>
16508              * <li>data - The drag data from the drag source</li>
16509              * <li>point - The point of the drop - append, above or below</li>
16510              * <li>source - The drag source</li>
16511              * <li>rawEvent - Raw mouse event</li>
16512              * <li>dropNode - Drop node(s) provided by the source.</li>
16513              * <li>cancel - Set this to true to signal drop not allowed.</li>
16514              * </ul>
16515              * @param {Object} dragOverEvent
16516              */
16517             "nodedragover" : true
16518         
16519    });
16520    if(this.singleExpand){
16521        this.on("beforeexpand", this.restrictExpand, this);
16522    }
16523 };
16524 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16525     rootVisible : true,
16526     animate: Roo.enableFx,
16527     lines : true,
16528     enableDD : false,
16529     hlDrop : Roo.enableFx,
16530   
16531     renderer: false,
16532     
16533     rendererTip: false,
16534     // private
16535     restrictExpand : function(node){
16536         var p = node.parentNode;
16537         if(p){
16538             if(p.expandedChild && p.expandedChild.parentNode == p){
16539                 p.expandedChild.collapse();
16540             }
16541             p.expandedChild = node;
16542         }
16543     },
16544
16545     // private override
16546     setRootNode : function(node){
16547         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16548         if(!this.rootVisible){
16549             node.ui = new Roo.tree.RootTreeNodeUI(node);
16550         }
16551         return node;
16552     },
16553
16554     /**
16555      * Returns the container element for this TreePanel
16556      */
16557     getEl : function(){
16558         return this.el;
16559     },
16560
16561     /**
16562      * Returns the default TreeLoader for this TreePanel
16563      */
16564     getLoader : function(){
16565         return this.loader;
16566     },
16567
16568     /**
16569      * Expand all nodes
16570      */
16571     expandAll : function(){
16572         this.root.expand(true);
16573     },
16574
16575     /**
16576      * Collapse all nodes
16577      */
16578     collapseAll : function(){
16579         this.root.collapse(true);
16580     },
16581
16582     /**
16583      * Returns the selection model used by this TreePanel
16584      */
16585     getSelectionModel : function(){
16586         if(!this.selModel){
16587             this.selModel = new Roo.tree.DefaultSelectionModel();
16588         }
16589         return this.selModel;
16590     },
16591
16592     /**
16593      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16594      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16595      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16596      * @return {Array}
16597      */
16598     getChecked : function(a, startNode){
16599         startNode = startNode || this.root;
16600         var r = [];
16601         var f = function(){
16602             if(this.attributes.checked){
16603                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16604             }
16605         }
16606         startNode.cascade(f);
16607         return r;
16608     },
16609
16610     /**
16611      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16612      * @param {String} path
16613      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16614      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16615      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16616      */
16617     expandPath : function(path, attr, callback){
16618         attr = attr || "id";
16619         var keys = path.split(this.pathSeparator);
16620         var curNode = this.root;
16621         if(curNode.attributes[attr] != keys[1]){ // invalid root
16622             if(callback){
16623                 callback(false, null);
16624             }
16625             return;
16626         }
16627         var index = 1;
16628         var f = function(){
16629             if(++index == keys.length){
16630                 if(callback){
16631                     callback(true, curNode);
16632                 }
16633                 return;
16634             }
16635             var c = curNode.findChild(attr, keys[index]);
16636             if(!c){
16637                 if(callback){
16638                     callback(false, curNode);
16639                 }
16640                 return;
16641             }
16642             curNode = c;
16643             c.expand(false, false, f);
16644         };
16645         curNode.expand(false, false, f);
16646     },
16647
16648     /**
16649      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16650      * @param {String} path
16651      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16652      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16653      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16654      */
16655     selectPath : function(path, attr, callback){
16656         attr = attr || "id";
16657         var keys = path.split(this.pathSeparator);
16658         var v = keys.pop();
16659         if(keys.length > 0){
16660             var f = function(success, node){
16661                 if(success && node){
16662                     var n = node.findChild(attr, v);
16663                     if(n){
16664                         n.select();
16665                         if(callback){
16666                             callback(true, n);
16667                         }
16668                     }else if(callback){
16669                         callback(false, n);
16670                     }
16671                 }else{
16672                     if(callback){
16673                         callback(false, n);
16674                     }
16675                 }
16676             };
16677             this.expandPath(keys.join(this.pathSeparator), attr, f);
16678         }else{
16679             this.root.select();
16680             if(callback){
16681                 callback(true, this.root);
16682             }
16683         }
16684     },
16685
16686     getTreeEl : function(){
16687         return this.el;
16688     },
16689
16690     /**
16691      * Trigger rendering of this TreePanel
16692      */
16693     render : function(){
16694         if (this.innerCt) {
16695             return this; // stop it rendering more than once!!
16696         }
16697         
16698         this.innerCt = this.el.createChild({tag:"ul",
16699                cls:"x-tree-root-ct " +
16700                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16701
16702         if(this.containerScroll){
16703             Roo.dd.ScrollManager.register(this.el);
16704         }
16705         if((this.enableDD || this.enableDrop) && !this.dropZone){
16706            /**
16707             * The dropZone used by this tree if drop is enabled
16708             * @type Roo.tree.TreeDropZone
16709             */
16710              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16711                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16712            });
16713         }
16714         if((this.enableDD || this.enableDrag) && !this.dragZone){
16715            /**
16716             * The dragZone used by this tree if drag is enabled
16717             * @type Roo.tree.TreeDragZone
16718             */
16719             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16720                ddGroup: this.ddGroup || "TreeDD",
16721                scroll: this.ddScroll
16722            });
16723         }
16724         this.getSelectionModel().init(this);
16725         if (!this.root) {
16726             console.log("ROOT not set in tree");
16727             return;
16728         }
16729         this.root.render();
16730         if(!this.rootVisible){
16731             this.root.renderChildren();
16732         }
16733         return this;
16734     }
16735 });/*
16736  * Based on:
16737  * Ext JS Library 1.1.1
16738  * Copyright(c) 2006-2007, Ext JS, LLC.
16739  *
16740  * Originally Released Under LGPL - original licence link has changed is not relivant.
16741  *
16742  * Fork - LGPL
16743  * <script type="text/javascript">
16744  */
16745  
16746
16747 /**
16748  * @class Roo.tree.DefaultSelectionModel
16749  * @extends Roo.util.Observable
16750  * The default single selection for a TreePanel.
16751  */
16752 Roo.tree.DefaultSelectionModel = function(){
16753    this.selNode = null;
16754    
16755    this.addEvents({
16756        /**
16757         * @event selectionchange
16758         * Fires when the selected node changes
16759         * @param {DefaultSelectionModel} this
16760         * @param {TreeNode} node the new selection
16761         */
16762        "selectionchange" : true,
16763
16764        /**
16765         * @event beforeselect
16766         * Fires before the selected node changes, return false to cancel the change
16767         * @param {DefaultSelectionModel} this
16768         * @param {TreeNode} node the new selection
16769         * @param {TreeNode} node the old selection
16770         */
16771        "beforeselect" : true
16772    });
16773 };
16774
16775 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16776     init : function(tree){
16777         this.tree = tree;
16778         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16779         tree.on("click", this.onNodeClick, this);
16780     },
16781     
16782     onNodeClick : function(node, e){
16783         if (e.ctrlKey && this.selNode == node)  {
16784             this.unselect(node);
16785             return;
16786         }
16787         this.select(node);
16788     },
16789     
16790     /**
16791      * Select a node.
16792      * @param {TreeNode} node The node to select
16793      * @return {TreeNode} The selected node
16794      */
16795     select : function(node){
16796         var last = this.selNode;
16797         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16798             if(last){
16799                 last.ui.onSelectedChange(false);
16800             }
16801             this.selNode = node;
16802             node.ui.onSelectedChange(true);
16803             this.fireEvent("selectionchange", this, node, last);
16804         }
16805         return node;
16806     },
16807     
16808     /**
16809      * Deselect a node.
16810      * @param {TreeNode} node The node to unselect
16811      */
16812     unselect : function(node){
16813         if(this.selNode == node){
16814             this.clearSelections();
16815         }    
16816     },
16817     
16818     /**
16819      * Clear all selections
16820      */
16821     clearSelections : function(){
16822         var n = this.selNode;
16823         if(n){
16824             n.ui.onSelectedChange(false);
16825             this.selNode = null;
16826             this.fireEvent("selectionchange", this, null);
16827         }
16828         return n;
16829     },
16830     
16831     /**
16832      * Get the selected node
16833      * @return {TreeNode} The selected node
16834      */
16835     getSelectedNode : function(){
16836         return this.selNode;    
16837     },
16838     
16839     /**
16840      * Returns true if the node is selected
16841      * @param {TreeNode} node The node to check
16842      * @return {Boolean}
16843      */
16844     isSelected : function(node){
16845         return this.selNode == node;  
16846     },
16847
16848     /**
16849      * Selects the node above the selected node in the tree, intelligently walking the nodes
16850      * @return TreeNode The new selection
16851      */
16852     selectPrevious : function(){
16853         var s = this.selNode || this.lastSelNode;
16854         if(!s){
16855             return null;
16856         }
16857         var ps = s.previousSibling;
16858         if(ps){
16859             if(!ps.isExpanded() || ps.childNodes.length < 1){
16860                 return this.select(ps);
16861             } else{
16862                 var lc = ps.lastChild;
16863                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
16864                     lc = lc.lastChild;
16865                 }
16866                 return this.select(lc);
16867             }
16868         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
16869             return this.select(s.parentNode);
16870         }
16871         return null;
16872     },
16873
16874     /**
16875      * Selects the node above the selected node in the tree, intelligently walking the nodes
16876      * @return TreeNode The new selection
16877      */
16878     selectNext : function(){
16879         var s = this.selNode || this.lastSelNode;
16880         if(!s){
16881             return null;
16882         }
16883         if(s.firstChild && s.isExpanded()){
16884              return this.select(s.firstChild);
16885          }else if(s.nextSibling){
16886              return this.select(s.nextSibling);
16887          }else if(s.parentNode){
16888             var newS = null;
16889             s.parentNode.bubble(function(){
16890                 if(this.nextSibling){
16891                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
16892                     return false;
16893                 }
16894             });
16895             return newS;
16896          }
16897         return null;
16898     },
16899
16900     onKeyDown : function(e){
16901         var s = this.selNode || this.lastSelNode;
16902         // undesirable, but required
16903         var sm = this;
16904         if(!s){
16905             return;
16906         }
16907         var k = e.getKey();
16908         switch(k){
16909              case e.DOWN:
16910                  e.stopEvent();
16911                  this.selectNext();
16912              break;
16913              case e.UP:
16914                  e.stopEvent();
16915                  this.selectPrevious();
16916              break;
16917              case e.RIGHT:
16918                  e.preventDefault();
16919                  if(s.hasChildNodes()){
16920                      if(!s.isExpanded()){
16921                          s.expand();
16922                      }else if(s.firstChild){
16923                          this.select(s.firstChild, e);
16924                      }
16925                  }
16926              break;
16927              case e.LEFT:
16928                  e.preventDefault();
16929                  if(s.hasChildNodes() && s.isExpanded()){
16930                      s.collapse();
16931                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
16932                      this.select(s.parentNode, e);
16933                  }
16934              break;
16935         };
16936     }
16937 });
16938
16939 /**
16940  * @class Roo.tree.MultiSelectionModel
16941  * @extends Roo.util.Observable
16942  * Multi selection for a TreePanel.
16943  */
16944 Roo.tree.MultiSelectionModel = function(){
16945    this.selNodes = [];
16946    this.selMap = {};
16947    this.addEvents({
16948        /**
16949         * @event selectionchange
16950         * Fires when the selected nodes change
16951         * @param {MultiSelectionModel} this
16952         * @param {Array} nodes Array of the selected nodes
16953         */
16954        "selectionchange" : true
16955    });
16956 };
16957
16958 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
16959     init : function(tree){
16960         this.tree = tree;
16961         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16962         tree.on("click", this.onNodeClick, this);
16963     },
16964     
16965     onNodeClick : function(node, e){
16966         this.select(node, e, e.ctrlKey);
16967     },
16968     
16969     /**
16970      * Select a node.
16971      * @param {TreeNode} node The node to select
16972      * @param {EventObject} e (optional) An event associated with the selection
16973      * @param {Boolean} keepExisting True to retain existing selections
16974      * @return {TreeNode} The selected node
16975      */
16976     select : function(node, e, keepExisting){
16977         if(keepExisting !== true){
16978             this.clearSelections(true);
16979         }
16980         if(this.isSelected(node)){
16981             this.lastSelNode = node;
16982             return node;
16983         }
16984         this.selNodes.push(node);
16985         this.selMap[node.id] = node;
16986         this.lastSelNode = node;
16987         node.ui.onSelectedChange(true);
16988         this.fireEvent("selectionchange", this, this.selNodes);
16989         return node;
16990     },
16991     
16992     /**
16993      * Deselect a node.
16994      * @param {TreeNode} node The node to unselect
16995      */
16996     unselect : function(node){
16997         if(this.selMap[node.id]){
16998             node.ui.onSelectedChange(false);
16999             var sn = this.selNodes;
17000             var index = -1;
17001             if(sn.indexOf){
17002                 index = sn.indexOf(node);
17003             }else{
17004                 for(var i = 0, len = sn.length; i < len; i++){
17005                     if(sn[i] == node){
17006                         index = i;
17007                         break;
17008                     }
17009                 }
17010             }
17011             if(index != -1){
17012                 this.selNodes.splice(index, 1);
17013             }
17014             delete this.selMap[node.id];
17015             this.fireEvent("selectionchange", this, this.selNodes);
17016         }
17017     },
17018     
17019     /**
17020      * Clear all selections
17021      */
17022     clearSelections : function(suppressEvent){
17023         var sn = this.selNodes;
17024         if(sn.length > 0){
17025             for(var i = 0, len = sn.length; i < len; i++){
17026                 sn[i].ui.onSelectedChange(false);
17027             }
17028             this.selNodes = [];
17029             this.selMap = {};
17030             if(suppressEvent !== true){
17031                 this.fireEvent("selectionchange", this, this.selNodes);
17032             }
17033         }
17034     },
17035     
17036     /**
17037      * Returns true if the node is selected
17038      * @param {TreeNode} node The node to check
17039      * @return {Boolean}
17040      */
17041     isSelected : function(node){
17042         return this.selMap[node.id] ? true : false;  
17043     },
17044     
17045     /**
17046      * Returns an array of the selected nodes
17047      * @return {Array}
17048      */
17049     getSelectedNodes : function(){
17050         return this.selNodes;    
17051     },
17052
17053     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17054
17055     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17056
17057     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17058 });/*
17059  * Based on:
17060  * Ext JS Library 1.1.1
17061  * Copyright(c) 2006-2007, Ext JS, LLC.
17062  *
17063  * Originally Released Under LGPL - original licence link has changed is not relivant.
17064  *
17065  * Fork - LGPL
17066  * <script type="text/javascript">
17067  */
17068  
17069 /**
17070  * @class Roo.tree.TreeNode
17071  * @extends Roo.data.Node
17072  * @cfg {String} text The text for this node
17073  * @cfg {Boolean} expanded true to start the node expanded
17074  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17075  * @cfg {Boolean} allowDrop false if this node cannot be drop on
17076  * @cfg {Boolean} disabled true to start the node disabled
17077  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17078  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17079  * @cfg {String} cls A css class to be added to the node
17080  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17081  * @cfg {String} href URL of the link used for the node (defaults to #)
17082  * @cfg {String} hrefTarget target frame for the link
17083  * @cfg {String} qtip An Ext QuickTip for the node
17084  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17085  * @cfg {Boolean} singleClickExpand True for single click expand on this node
17086  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17087  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17088  * (defaults to undefined with no checkbox rendered)
17089  * @constructor
17090  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17091  */
17092 Roo.tree.TreeNode = function(attributes){
17093     attributes = attributes || {};
17094     if(typeof attributes == "string"){
17095         attributes = {text: attributes};
17096     }
17097     this.childrenRendered = false;
17098     this.rendered = false;
17099     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17100     this.expanded = attributes.expanded === true;
17101     this.isTarget = attributes.isTarget !== false;
17102     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17103     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17104
17105     /**
17106      * Read-only. The text for this node. To change it use setText().
17107      * @type String
17108      */
17109     this.text = attributes.text;
17110     /**
17111      * True if this node is disabled.
17112      * @type Boolean
17113      */
17114     this.disabled = attributes.disabled === true;
17115
17116     this.addEvents({
17117         /**
17118         * @event textchange
17119         * Fires when the text for this node is changed
17120         * @param {Node} this This node
17121         * @param {String} text The new text
17122         * @param {String} oldText The old text
17123         */
17124         "textchange" : true,
17125         /**
17126         * @event beforeexpand
17127         * Fires before this node is expanded, return false to cancel.
17128         * @param {Node} this This node
17129         * @param {Boolean} deep
17130         * @param {Boolean} anim
17131         */
17132         "beforeexpand" : true,
17133         /**
17134         * @event beforecollapse
17135         * Fires before this node is collapsed, return false to cancel.
17136         * @param {Node} this This node
17137         * @param {Boolean} deep
17138         * @param {Boolean} anim
17139         */
17140         "beforecollapse" : true,
17141         /**
17142         * @event expand
17143         * Fires when this node is expanded
17144         * @param {Node} this This node
17145         */
17146         "expand" : true,
17147         /**
17148         * @event disabledchange
17149         * Fires when the disabled status of this node changes
17150         * @param {Node} this This node
17151         * @param {Boolean} disabled
17152         */
17153         "disabledchange" : true,
17154         /**
17155         * @event collapse
17156         * Fires when this node is collapsed
17157         * @param {Node} this This node
17158         */
17159         "collapse" : true,
17160         /**
17161         * @event beforeclick
17162         * Fires before click processing. Return false to cancel the default action.
17163         * @param {Node} this This node
17164         * @param {Roo.EventObject} e The event object
17165         */
17166         "beforeclick":true,
17167         /**
17168         * @event checkchange
17169         * Fires when a node with a checkbox's checked property changes
17170         * @param {Node} this This node
17171         * @param {Boolean} checked
17172         */
17173         "checkchange":true,
17174         /**
17175         * @event click
17176         * Fires when this node is clicked
17177         * @param {Node} this This node
17178         * @param {Roo.EventObject} e The event object
17179         */
17180         "click":true,
17181         /**
17182         * @event dblclick
17183         * Fires when this node is double clicked
17184         * @param {Node} this This node
17185         * @param {Roo.EventObject} e The event object
17186         */
17187         "dblclick":true,
17188         /**
17189         * @event contextmenu
17190         * Fires when this node is right clicked
17191         * @param {Node} this This node
17192         * @param {Roo.EventObject} e The event object
17193         */
17194         "contextmenu":true,
17195         /**
17196         * @event beforechildrenrendered
17197         * Fires right before the child nodes for this node are rendered
17198         * @param {Node} this This node
17199         */
17200         "beforechildrenrendered":true
17201     });
17202
17203     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17204
17205     /**
17206      * Read-only. The UI for this node
17207      * @type TreeNodeUI
17208      */
17209     this.ui = new uiClass(this);
17210 };
17211 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17212     preventHScroll: true,
17213     /**
17214      * Returns true if this node is expanded
17215      * @return {Boolean}
17216      */
17217     isExpanded : function(){
17218         return this.expanded;
17219     },
17220
17221     /**
17222      * Returns the UI object for this node
17223      * @return {TreeNodeUI}
17224      */
17225     getUI : function(){
17226         return this.ui;
17227     },
17228
17229     // private override
17230     setFirstChild : function(node){
17231         var of = this.firstChild;
17232         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17233         if(this.childrenRendered && of && node != of){
17234             of.renderIndent(true, true);
17235         }
17236         if(this.rendered){
17237             this.renderIndent(true, true);
17238         }
17239     },
17240
17241     // private override
17242     setLastChild : function(node){
17243         var ol = this.lastChild;
17244         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17245         if(this.childrenRendered && ol && node != ol){
17246             ol.renderIndent(true, true);
17247         }
17248         if(this.rendered){
17249             this.renderIndent(true, true);
17250         }
17251     },
17252
17253     // these methods are overridden to provide lazy rendering support
17254     // private override
17255     appendChild : function(){
17256         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17257         if(node && this.childrenRendered){
17258             node.render();
17259         }
17260         this.ui.updateExpandIcon();
17261         return node;
17262     },
17263
17264     // private override
17265     removeChild : function(node){
17266         this.ownerTree.getSelectionModel().unselect(node);
17267         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17268         // if it's been rendered remove dom node
17269         if(this.childrenRendered){
17270             node.ui.remove();
17271         }
17272         if(this.childNodes.length < 1){
17273             this.collapse(false, false);
17274         }else{
17275             this.ui.updateExpandIcon();
17276         }
17277         if(!this.firstChild) {
17278             this.childrenRendered = false;
17279         }
17280         return node;
17281     },
17282
17283     // private override
17284     insertBefore : function(node, refNode){
17285         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17286         if(newNode && refNode && this.childrenRendered){
17287             node.render();
17288         }
17289         this.ui.updateExpandIcon();
17290         return newNode;
17291     },
17292
17293     /**
17294      * Sets the text for this node
17295      * @param {String} text
17296      */
17297     setText : function(text){
17298         var oldText = this.text;
17299         this.text = text;
17300         this.attributes.text = text;
17301         if(this.rendered){ // event without subscribing
17302             this.ui.onTextChange(this, text, oldText);
17303         }
17304         this.fireEvent("textchange", this, text, oldText);
17305     },
17306
17307     /**
17308      * Triggers selection of this node
17309      */
17310     select : function(){
17311         this.getOwnerTree().getSelectionModel().select(this);
17312     },
17313
17314     /**
17315      * Triggers deselection of this node
17316      */
17317     unselect : function(){
17318         this.getOwnerTree().getSelectionModel().unselect(this);
17319     },
17320
17321     /**
17322      * Returns true if this node is selected
17323      * @return {Boolean}
17324      */
17325     isSelected : function(){
17326         return this.getOwnerTree().getSelectionModel().isSelected(this);
17327     },
17328
17329     /**
17330      * Expand this node.
17331      * @param {Boolean} deep (optional) True to expand all children as well
17332      * @param {Boolean} anim (optional) false to cancel the default animation
17333      * @param {Function} callback (optional) A callback to be called when
17334      * expanding this node completes (does not wait for deep expand to complete).
17335      * Called with 1 parameter, this node.
17336      */
17337     expand : function(deep, anim, callback){
17338         if(!this.expanded){
17339             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17340                 return;
17341             }
17342             if(!this.childrenRendered){
17343                 this.renderChildren();
17344             }
17345             this.expanded = true;
17346             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17347                 this.ui.animExpand(function(){
17348                     this.fireEvent("expand", this);
17349                     if(typeof callback == "function"){
17350                         callback(this);
17351                     }
17352                     if(deep === true){
17353                         this.expandChildNodes(true);
17354                     }
17355                 }.createDelegate(this));
17356                 return;
17357             }else{
17358                 this.ui.expand();
17359                 this.fireEvent("expand", this);
17360                 if(typeof callback == "function"){
17361                     callback(this);
17362                 }
17363             }
17364         }else{
17365            if(typeof callback == "function"){
17366                callback(this);
17367            }
17368         }
17369         if(deep === true){
17370             this.expandChildNodes(true);
17371         }
17372     },
17373
17374     isHiddenRoot : function(){
17375         return this.isRoot && !this.getOwnerTree().rootVisible;
17376     },
17377
17378     /**
17379      * Collapse this node.
17380      * @param {Boolean} deep (optional) True to collapse all children as well
17381      * @param {Boolean} anim (optional) false to cancel the default animation
17382      */
17383     collapse : function(deep, anim){
17384         if(this.expanded && !this.isHiddenRoot()){
17385             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17386                 return;
17387             }
17388             this.expanded = false;
17389             if((this.getOwnerTree().animate && anim !== false) || anim){
17390                 this.ui.animCollapse(function(){
17391                     this.fireEvent("collapse", this);
17392                     if(deep === true){
17393                         this.collapseChildNodes(true);
17394                     }
17395                 }.createDelegate(this));
17396                 return;
17397             }else{
17398                 this.ui.collapse();
17399                 this.fireEvent("collapse", this);
17400             }
17401         }
17402         if(deep === true){
17403             var cs = this.childNodes;
17404             for(var i = 0, len = cs.length; i < len; i++) {
17405                 cs[i].collapse(true, false);
17406             }
17407         }
17408     },
17409
17410     // private
17411     delayedExpand : function(delay){
17412         if(!this.expandProcId){
17413             this.expandProcId = this.expand.defer(delay, this);
17414         }
17415     },
17416
17417     // private
17418     cancelExpand : function(){
17419         if(this.expandProcId){
17420             clearTimeout(this.expandProcId);
17421         }
17422         this.expandProcId = false;
17423     },
17424
17425     /**
17426      * Toggles expanded/collapsed state of the node
17427      */
17428     toggle : function(){
17429         if(this.expanded){
17430             this.collapse();
17431         }else{
17432             this.expand();
17433         }
17434     },
17435
17436     /**
17437      * Ensures all parent nodes are expanded
17438      */
17439     ensureVisible : function(callback){
17440         var tree = this.getOwnerTree();
17441         tree.expandPath(this.parentNode.getPath(), false, function(){
17442             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17443             Roo.callback(callback);
17444         }.createDelegate(this));
17445     },
17446
17447     /**
17448      * Expand all child nodes
17449      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17450      */
17451     expandChildNodes : function(deep){
17452         var cs = this.childNodes;
17453         for(var i = 0, len = cs.length; i < len; i++) {
17454                 cs[i].expand(deep);
17455         }
17456     },
17457
17458     /**
17459      * Collapse all child nodes
17460      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17461      */
17462     collapseChildNodes : function(deep){
17463         var cs = this.childNodes;
17464         for(var i = 0, len = cs.length; i < len; i++) {
17465                 cs[i].collapse(deep);
17466         }
17467     },
17468
17469     /**
17470      * Disables this node
17471      */
17472     disable : function(){
17473         this.disabled = true;
17474         this.unselect();
17475         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17476             this.ui.onDisableChange(this, true);
17477         }
17478         this.fireEvent("disabledchange", this, true);
17479     },
17480
17481     /**
17482      * Enables this node
17483      */
17484     enable : function(){
17485         this.disabled = false;
17486         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17487             this.ui.onDisableChange(this, false);
17488         }
17489         this.fireEvent("disabledchange", this, false);
17490     },
17491
17492     // private
17493     renderChildren : function(suppressEvent){
17494         if(suppressEvent !== false){
17495             this.fireEvent("beforechildrenrendered", this);
17496         }
17497         var cs = this.childNodes;
17498         for(var i = 0, len = cs.length; i < len; i++){
17499             cs[i].render(true);
17500         }
17501         this.childrenRendered = true;
17502     },
17503
17504     // private
17505     sort : function(fn, scope){
17506         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17507         if(this.childrenRendered){
17508             var cs = this.childNodes;
17509             for(var i = 0, len = cs.length; i < len; i++){
17510                 cs[i].render(true);
17511             }
17512         }
17513     },
17514
17515     // private
17516     render : function(bulkRender){
17517         this.ui.render(bulkRender);
17518         if(!this.rendered){
17519             this.rendered = true;
17520             if(this.expanded){
17521                 this.expanded = false;
17522                 this.expand(false, false);
17523             }
17524         }
17525     },
17526
17527     // private
17528     renderIndent : function(deep, refresh){
17529         if(refresh){
17530             this.ui.childIndent = null;
17531         }
17532         this.ui.renderIndent();
17533         if(deep === true && this.childrenRendered){
17534             var cs = this.childNodes;
17535             for(var i = 0, len = cs.length; i < len; i++){
17536                 cs[i].renderIndent(true, refresh);
17537             }
17538         }
17539     }
17540 });/*
17541  * Based on:
17542  * Ext JS Library 1.1.1
17543  * Copyright(c) 2006-2007, Ext JS, LLC.
17544  *
17545  * Originally Released Under LGPL - original licence link has changed is not relivant.
17546  *
17547  * Fork - LGPL
17548  * <script type="text/javascript">
17549  */
17550  
17551 /**
17552  * @class Roo.tree.AsyncTreeNode
17553  * @extends Roo.tree.TreeNode
17554  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17555  * @constructor
17556  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17557  */
17558  Roo.tree.AsyncTreeNode = function(config){
17559     this.loaded = false;
17560     this.loading = false;
17561     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17562     /**
17563     * @event beforeload
17564     * Fires before this node is loaded, return false to cancel
17565     * @param {Node} this This node
17566     */
17567     this.addEvents({'beforeload':true, 'load': true});
17568     /**
17569     * @event load
17570     * Fires when this node is loaded
17571     * @param {Node} this This node
17572     */
17573     /**
17574      * The loader used by this node (defaults to using the tree's defined loader)
17575      * @type TreeLoader
17576      * @property loader
17577      */
17578 };
17579 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17580     expand : function(deep, anim, callback){
17581         if(this.loading){ // if an async load is already running, waiting til it's done
17582             var timer;
17583             var f = function(){
17584                 if(!this.loading){ // done loading
17585                     clearInterval(timer);
17586                     this.expand(deep, anim, callback);
17587                 }
17588             }.createDelegate(this);
17589             timer = setInterval(f, 200);
17590             return;
17591         }
17592         if(!this.loaded){
17593             if(this.fireEvent("beforeload", this) === false){
17594                 return;
17595             }
17596             this.loading = true;
17597             this.ui.beforeLoad(this);
17598             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17599             if(loader){
17600                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17601                 return;
17602             }
17603         }
17604         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17605     },
17606     
17607     /**
17608      * Returns true if this node is currently loading
17609      * @return {Boolean}
17610      */
17611     isLoading : function(){
17612         return this.loading;  
17613     },
17614     
17615     loadComplete : function(deep, anim, callback){
17616         this.loading = false;
17617         this.loaded = true;
17618         this.ui.afterLoad(this);
17619         this.fireEvent("load", this);
17620         this.expand(deep, anim, callback);
17621     },
17622     
17623     /**
17624      * Returns true if this node has been loaded
17625      * @return {Boolean}
17626      */
17627     isLoaded : function(){
17628         return this.loaded;
17629     },
17630     
17631     hasChildNodes : function(){
17632         if(!this.isLeaf() && !this.loaded){
17633             return true;
17634         }else{
17635             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17636         }
17637     },
17638
17639     /**
17640      * Trigger a reload for this node
17641      * @param {Function} callback
17642      */
17643     reload : function(callback){
17644         this.collapse(false, false);
17645         while(this.firstChild){
17646             this.removeChild(this.firstChild);
17647         }
17648         this.childrenRendered = false;
17649         this.loaded = false;
17650         if(this.isHiddenRoot()){
17651             this.expanded = false;
17652         }
17653         this.expand(false, false, callback);
17654     }
17655 });/*
17656  * Based on:
17657  * Ext JS Library 1.1.1
17658  * Copyright(c) 2006-2007, Ext JS, LLC.
17659  *
17660  * Originally Released Under LGPL - original licence link has changed is not relivant.
17661  *
17662  * Fork - LGPL
17663  * <script type="text/javascript">
17664  */
17665  
17666 /**
17667  * @class Roo.tree.TreeNodeUI
17668  * @constructor
17669  * @param {Object} node The node to render
17670  * The TreeNode UI implementation is separate from the
17671  * tree implementation. Unless you are customizing the tree UI,
17672  * you should never have to use this directly.
17673  */
17674 Roo.tree.TreeNodeUI = function(node){
17675     this.node = node;
17676     this.rendered = false;
17677     this.animating = false;
17678     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17679 };
17680
17681 Roo.tree.TreeNodeUI.prototype = {
17682     removeChild : function(node){
17683         if(this.rendered){
17684             this.ctNode.removeChild(node.ui.getEl());
17685         }
17686     },
17687
17688     beforeLoad : function(){
17689          this.addClass("x-tree-node-loading");
17690     },
17691
17692     afterLoad : function(){
17693          this.removeClass("x-tree-node-loading");
17694     },
17695
17696     onTextChange : function(node, text, oldText){
17697         if(this.rendered){
17698             this.textNode.innerHTML = text;
17699         }
17700     },
17701
17702     onDisableChange : function(node, state){
17703         this.disabled = state;
17704         if(state){
17705             this.addClass("x-tree-node-disabled");
17706         }else{
17707             this.removeClass("x-tree-node-disabled");
17708         }
17709     },
17710
17711     onSelectedChange : function(state){
17712         if(state){
17713             this.focus();
17714             this.addClass("x-tree-selected");
17715         }else{
17716             //this.blur();
17717             this.removeClass("x-tree-selected");
17718         }
17719     },
17720
17721     onMove : function(tree, node, oldParent, newParent, index, refNode){
17722         this.childIndent = null;
17723         if(this.rendered){
17724             var targetNode = newParent.ui.getContainer();
17725             if(!targetNode){//target not rendered
17726                 this.holder = document.createElement("div");
17727                 this.holder.appendChild(this.wrap);
17728                 return;
17729             }
17730             var insertBefore = refNode ? refNode.ui.getEl() : null;
17731             if(insertBefore){
17732                 targetNode.insertBefore(this.wrap, insertBefore);
17733             }else{
17734                 targetNode.appendChild(this.wrap);
17735             }
17736             this.node.renderIndent(true);
17737         }
17738     },
17739
17740     addClass : function(cls){
17741         if(this.elNode){
17742             Roo.fly(this.elNode).addClass(cls);
17743         }
17744     },
17745
17746     removeClass : function(cls){
17747         if(this.elNode){
17748             Roo.fly(this.elNode).removeClass(cls);
17749         }
17750     },
17751
17752     remove : function(){
17753         if(this.rendered){
17754             this.holder = document.createElement("div");
17755             this.holder.appendChild(this.wrap);
17756         }
17757     },
17758
17759     fireEvent : function(){
17760         return this.node.fireEvent.apply(this.node, arguments);
17761     },
17762
17763     initEvents : function(){
17764         this.node.on("move", this.onMove, this);
17765         var E = Roo.EventManager;
17766         var a = this.anchor;
17767
17768         var el = Roo.fly(a, '_treeui');
17769
17770         if(Roo.isOpera){ // opera render bug ignores the CSS
17771             el.setStyle("text-decoration", "none");
17772         }
17773
17774         el.on("click", this.onClick, this);
17775         el.on("dblclick", this.onDblClick, this);
17776
17777         if(this.checkbox){
17778             Roo.EventManager.on(this.checkbox,
17779                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17780         }
17781
17782         el.on("contextmenu", this.onContextMenu, this);
17783
17784         var icon = Roo.fly(this.iconNode);
17785         icon.on("click", this.onClick, this);
17786         icon.on("dblclick", this.onDblClick, this);
17787         icon.on("contextmenu", this.onContextMenu, this);
17788         E.on(this.ecNode, "click", this.ecClick, this, true);
17789
17790         if(this.node.disabled){
17791             this.addClass("x-tree-node-disabled");
17792         }
17793         if(this.node.hidden){
17794             this.addClass("x-tree-node-disabled");
17795         }
17796         var ot = this.node.getOwnerTree();
17797         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17798         if(dd && (!this.node.isRoot || ot.rootVisible)){
17799             Roo.dd.Registry.register(this.elNode, {
17800                 node: this.node,
17801                 handles: this.getDDHandles(),
17802                 isHandle: false
17803             });
17804         }
17805     },
17806
17807     getDDHandles : function(){
17808         return [this.iconNode, this.textNode];
17809     },
17810
17811     hide : function(){
17812         if(this.rendered){
17813             this.wrap.style.display = "none";
17814         }
17815     },
17816
17817     show : function(){
17818         if(this.rendered){
17819             this.wrap.style.display = "";
17820         }
17821     },
17822
17823     onContextMenu : function(e){
17824         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
17825             e.preventDefault();
17826             this.focus();
17827             this.fireEvent("contextmenu", this.node, e);
17828         }
17829     },
17830
17831     onClick : function(e){
17832         if(this.dropping){
17833             e.stopEvent();
17834             return;
17835         }
17836         if(this.fireEvent("beforeclick", this.node, e) !== false){
17837             if(!this.disabled && this.node.attributes.href){
17838                 this.fireEvent("click", this.node, e);
17839                 return;
17840             }
17841             e.preventDefault();
17842             if(this.disabled){
17843                 return;
17844             }
17845
17846             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
17847                 this.node.toggle();
17848             }
17849
17850             this.fireEvent("click", this.node, e);
17851         }else{
17852             e.stopEvent();
17853         }
17854     },
17855
17856     onDblClick : function(e){
17857         e.preventDefault();
17858         if(this.disabled){
17859             return;
17860         }
17861         if(this.checkbox){
17862             this.toggleCheck();
17863         }
17864         if(!this.animating && this.node.hasChildNodes()){
17865             this.node.toggle();
17866         }
17867         this.fireEvent("dblclick", this.node, e);
17868     },
17869
17870     onCheckChange : function(){
17871         var checked = this.checkbox.checked;
17872         this.node.attributes.checked = checked;
17873         this.fireEvent('checkchange', this.node, checked);
17874     },
17875
17876     ecClick : function(e){
17877         if(!this.animating && this.node.hasChildNodes()){
17878             this.node.toggle();
17879         }
17880     },
17881
17882     startDrop : function(){
17883         this.dropping = true;
17884     },
17885
17886     // delayed drop so the click event doesn't get fired on a drop
17887     endDrop : function(){
17888        setTimeout(function(){
17889            this.dropping = false;
17890        }.createDelegate(this), 50);
17891     },
17892
17893     expand : function(){
17894         this.updateExpandIcon();
17895         this.ctNode.style.display = "";
17896     },
17897
17898     focus : function(){
17899         if(!this.node.preventHScroll){
17900             try{this.anchor.focus();
17901             }catch(e){}
17902         }else if(!Roo.isIE){
17903             try{
17904                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
17905                 var l = noscroll.scrollLeft;
17906                 this.anchor.focus();
17907                 noscroll.scrollLeft = l;
17908             }catch(e){}
17909         }
17910     },
17911
17912     toggleCheck : function(value){
17913         var cb = this.checkbox;
17914         if(cb){
17915             cb.checked = (value === undefined ? !cb.checked : value);
17916         }
17917     },
17918
17919     blur : function(){
17920         try{
17921             this.anchor.blur();
17922         }catch(e){}
17923     },
17924
17925     animExpand : function(callback){
17926         var ct = Roo.get(this.ctNode);
17927         ct.stopFx();
17928         if(!this.node.hasChildNodes()){
17929             this.updateExpandIcon();
17930             this.ctNode.style.display = "";
17931             Roo.callback(callback);
17932             return;
17933         }
17934         this.animating = true;
17935         this.updateExpandIcon();
17936
17937         ct.slideIn('t', {
17938            callback : function(){
17939                this.animating = false;
17940                Roo.callback(callback);
17941             },
17942             scope: this,
17943             duration: this.node.ownerTree.duration || .25
17944         });
17945     },
17946
17947     highlight : function(){
17948         var tree = this.node.getOwnerTree();
17949         Roo.fly(this.wrap).highlight(
17950             tree.hlColor || "C3DAF9",
17951             {endColor: tree.hlBaseColor}
17952         );
17953     },
17954
17955     collapse : function(){
17956         this.updateExpandIcon();
17957         this.ctNode.style.display = "none";
17958     },
17959
17960     animCollapse : function(callback){
17961         var ct = Roo.get(this.ctNode);
17962         ct.enableDisplayMode('block');
17963         ct.stopFx();
17964
17965         this.animating = true;
17966         this.updateExpandIcon();
17967
17968         ct.slideOut('t', {
17969             callback : function(){
17970                this.animating = false;
17971                Roo.callback(callback);
17972             },
17973             scope: this,
17974             duration: this.node.ownerTree.duration || .25
17975         });
17976     },
17977
17978     getContainer : function(){
17979         return this.ctNode;
17980     },
17981
17982     getEl : function(){
17983         return this.wrap;
17984     },
17985
17986     appendDDGhost : function(ghostNode){
17987         ghostNode.appendChild(this.elNode.cloneNode(true));
17988     },
17989
17990     getDDRepairXY : function(){
17991         return Roo.lib.Dom.getXY(this.iconNode);
17992     },
17993
17994     onRender : function(){
17995         this.render();
17996     },
17997
17998     render : function(bulkRender){
17999         var n = this.node, a = n.attributes;
18000         var targetNode = n.parentNode ?
18001               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
18002
18003         if(!this.rendered){
18004             this.rendered = true;
18005
18006             this.renderElements(n, a, targetNode, bulkRender);
18007
18008             if(a.qtip){
18009                if(this.textNode.setAttributeNS){
18010                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
18011                    if(a.qtipTitle){
18012                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
18013                    }
18014                }else{
18015                    this.textNode.setAttribute("ext:qtip", a.qtip);
18016                    if(a.qtipTitle){
18017                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
18018                    }
18019                }
18020             }else if(a.qtipCfg){
18021                 a.qtipCfg.target = Roo.id(this.textNode);
18022                 Roo.QuickTips.register(a.qtipCfg);
18023             }
18024             this.initEvents();
18025             if(!this.node.expanded){
18026                 this.updateExpandIcon();
18027             }
18028         }else{
18029             if(bulkRender === true) {
18030                 targetNode.appendChild(this.wrap);
18031             }
18032         }
18033     },
18034
18035     renderElements : function(n, a, targetNode, bulkRender){
18036         // add some indent caching, this helps performance when rendering a large tree
18037         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18038         var t = n.getOwnerTree();
18039         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18040         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18041         var cb = typeof a.checked == 'boolean';
18042         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18043         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18044             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18045             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18046             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18047             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18048             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18049              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
18050                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18051             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18052             "</li>"];
18053
18054         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18055             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18056                                 n.nextSibling.ui.getEl(), buf.join(""));
18057         }else{
18058             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18059         }
18060
18061         this.elNode = this.wrap.childNodes[0];
18062         this.ctNode = this.wrap.childNodes[1];
18063         var cs = this.elNode.childNodes;
18064         this.indentNode = cs[0];
18065         this.ecNode = cs[1];
18066         this.iconNode = cs[2];
18067         var index = 3;
18068         if(cb){
18069             this.checkbox = cs[3];
18070             index++;
18071         }
18072         this.anchor = cs[index];
18073         this.textNode = cs[index].firstChild;
18074     },
18075
18076     getAnchor : function(){
18077         return this.anchor;
18078     },
18079
18080     getTextEl : function(){
18081         return this.textNode;
18082     },
18083
18084     getIconEl : function(){
18085         return this.iconNode;
18086     },
18087
18088     isChecked : function(){
18089         return this.checkbox ? this.checkbox.checked : false;
18090     },
18091
18092     updateExpandIcon : function(){
18093         if(this.rendered){
18094             var n = this.node, c1, c2;
18095             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18096             var hasChild = n.hasChildNodes();
18097             if(hasChild){
18098                 if(n.expanded){
18099                     cls += "-minus";
18100                     c1 = "x-tree-node-collapsed";
18101                     c2 = "x-tree-node-expanded";
18102                 }else{
18103                     cls += "-plus";
18104                     c1 = "x-tree-node-expanded";
18105                     c2 = "x-tree-node-collapsed";
18106                 }
18107                 if(this.wasLeaf){
18108                     this.removeClass("x-tree-node-leaf");
18109                     this.wasLeaf = false;
18110                 }
18111                 if(this.c1 != c1 || this.c2 != c2){
18112                     Roo.fly(this.elNode).replaceClass(c1, c2);
18113                     this.c1 = c1; this.c2 = c2;
18114                 }
18115             }else{
18116                 if(!this.wasLeaf){
18117                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18118                     delete this.c1;
18119                     delete this.c2;
18120                     this.wasLeaf = true;
18121                 }
18122             }
18123             var ecc = "x-tree-ec-icon "+cls;
18124             if(this.ecc != ecc){
18125                 this.ecNode.className = ecc;
18126                 this.ecc = ecc;
18127             }
18128         }
18129     },
18130
18131     getChildIndent : function(){
18132         if(!this.childIndent){
18133             var buf = [];
18134             var p = this.node;
18135             while(p){
18136                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18137                     if(!p.isLast()) {
18138                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18139                     } else {
18140                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18141                     }
18142                 }
18143                 p = p.parentNode;
18144             }
18145             this.childIndent = buf.join("");
18146         }
18147         return this.childIndent;
18148     },
18149
18150     renderIndent : function(){
18151         if(this.rendered){
18152             var indent = "";
18153             var p = this.node.parentNode;
18154             if(p){
18155                 indent = p.ui.getChildIndent();
18156             }
18157             if(this.indentMarkup != indent){ // don't rerender if not required
18158                 this.indentNode.innerHTML = indent;
18159                 this.indentMarkup = indent;
18160             }
18161             this.updateExpandIcon();
18162         }
18163     }
18164 };
18165
18166 Roo.tree.RootTreeNodeUI = function(){
18167     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18168 };
18169 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18170     render : function(){
18171         if(!this.rendered){
18172             var targetNode = this.node.ownerTree.innerCt.dom;
18173             this.node.expanded = true;
18174             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18175             this.wrap = this.ctNode = targetNode.firstChild;
18176         }
18177     },
18178     collapse : function(){
18179     },
18180     expand : function(){
18181     }
18182 });/*
18183  * Based on:
18184  * Ext JS Library 1.1.1
18185  * Copyright(c) 2006-2007, Ext JS, LLC.
18186  *
18187  * Originally Released Under LGPL - original licence link has changed is not relivant.
18188  *
18189  * Fork - LGPL
18190  * <script type="text/javascript">
18191  */
18192 /**
18193  * @class Roo.tree.TreeLoader
18194  * @extends Roo.util.Observable
18195  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18196  * nodes from a specified URL. The response must be a javascript Array definition
18197  * who's elements are node definition objects. eg:
18198  * <pre><code>
18199    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
18200     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
18201 </code></pre>
18202  * <br><br>
18203  * A server request is sent, and child nodes are loaded only when a node is expanded.
18204  * The loading node's id is passed to the server under the parameter name "node" to
18205  * enable the server to produce the correct child nodes.
18206  * <br><br>
18207  * To pass extra parameters, an event handler may be attached to the "beforeload"
18208  * event, and the parameters specified in the TreeLoader's baseParams property:
18209  * <pre><code>
18210     myTreeLoader.on("beforeload", function(treeLoader, node) {
18211         this.baseParams.category = node.attributes.category;
18212     }, this);
18213 </code></pre><
18214  * This would pass an HTTP parameter called "category" to the server containing
18215  * the value of the Node's "category" attribute.
18216  * @constructor
18217  * Creates a new Treeloader.
18218  * @param {Object} config A config object containing config properties.
18219  */
18220 Roo.tree.TreeLoader = function(config){
18221     this.baseParams = {};
18222     this.requestMethod = "POST";
18223     Roo.apply(this, config);
18224
18225     this.addEvents({
18226     
18227         /**
18228          * @event beforeload
18229          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18230          * @param {Object} This TreeLoader object.
18231          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18232          * @param {Object} callback The callback function specified in the {@link #load} call.
18233          */
18234         beforeload : true,
18235         /**
18236          * @event load
18237          * Fires when the node has been successfuly loaded.
18238          * @param {Object} This TreeLoader object.
18239          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18240          * @param {Object} response The response object containing the data from the server.
18241          */
18242         load : true,
18243         /**
18244          * @event loadexception
18245          * Fires if the network request failed.
18246          * @param {Object} This TreeLoader object.
18247          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18248          * @param {Object} response The response object containing the data from the server.
18249          */
18250         loadexception : true,
18251         /**
18252          * @event create
18253          * Fires before a node is created, enabling you to return custom Node types 
18254          * @param {Object} This TreeLoader object.
18255          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18256          */
18257         create : true
18258     });
18259
18260     Roo.tree.TreeLoader.superclass.constructor.call(this);
18261 };
18262
18263 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18264     /**
18265     * @cfg {String} dataUrl The URL from which to request a Json string which
18266     * specifies an array of node definition object representing the child nodes
18267     * to be loaded.
18268     */
18269     /**
18270     * @cfg {Object} baseParams (optional) An object containing properties which
18271     * specify HTTP parameters to be passed to each request for child nodes.
18272     */
18273     /**
18274     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18275     * created by this loader. If the attributes sent by the server have an attribute in this object,
18276     * they take priority.
18277     */
18278     /**
18279     * @cfg {Object} uiProviders (optional) An object containing properties which
18280     * 
18281     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
18282     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18283     * <i>uiProvider</i> attribute of a returned child node is a string rather
18284     * than a reference to a TreeNodeUI implementation, this that string value
18285     * is used as a property name in the uiProviders object. You can define the provider named
18286     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18287     */
18288     uiProviders : {},
18289
18290     /**
18291     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18292     * child nodes before loading.
18293     */
18294     clearOnLoad : true,
18295
18296     /**
18297     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18298     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18299     * Grid query { data : [ .....] }
18300     */
18301     
18302     root : false,
18303      /**
18304     * @cfg {String} queryParam (optional) 
18305     * Name of the query as it will be passed on the querystring (defaults to 'node')
18306     * eg. the request will be ?node=[id]
18307     */
18308     
18309     
18310     queryParam: false,
18311     
18312     /**
18313      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18314      * This is called automatically when a node is expanded, but may be used to reload
18315      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18316      * @param {Roo.tree.TreeNode} node
18317      * @param {Function} callback
18318      */
18319     load : function(node, callback){
18320         if(this.clearOnLoad){
18321             while(node.firstChild){
18322                 node.removeChild(node.firstChild);
18323             }
18324         }
18325         if(node.attributes.children){ // preloaded json children
18326             var cs = node.attributes.children;
18327             for(var i = 0, len = cs.length; i < len; i++){
18328                 node.appendChild(this.createNode(cs[i]));
18329             }
18330             if(typeof callback == "function"){
18331                 callback();
18332             }
18333         }else if(this.dataUrl){
18334             this.requestData(node, callback);
18335         }
18336     },
18337
18338     getParams: function(node){
18339         var buf = [], bp = this.baseParams;
18340         for(var key in bp){
18341             if(typeof bp[key] != "function"){
18342                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18343             }
18344         }
18345         var n = this.queryParam === false ? 'node' : this.queryParam;
18346         buf.push(n + "=", encodeURIComponent(node.id));
18347         return buf.join("");
18348     },
18349
18350     requestData : function(node, callback){
18351         if(this.fireEvent("beforeload", this, node, callback) !== false){
18352             this.transId = Roo.Ajax.request({
18353                 method:this.requestMethod,
18354                 url: this.dataUrl||this.url,
18355                 success: this.handleResponse,
18356                 failure: this.handleFailure,
18357                 scope: this,
18358                 argument: {callback: callback, node: node},
18359                 params: this.getParams(node)
18360             });
18361         }else{
18362             // if the load is cancelled, make sure we notify
18363             // the node that we are done
18364             if(typeof callback == "function"){
18365                 callback();
18366             }
18367         }
18368     },
18369
18370     isLoading : function(){
18371         return this.transId ? true : false;
18372     },
18373
18374     abort : function(){
18375         if(this.isLoading()){
18376             Roo.Ajax.abort(this.transId);
18377         }
18378     },
18379
18380     // private
18381     createNode : function(attr){
18382         // apply baseAttrs, nice idea Corey!
18383         if(this.baseAttrs){
18384             Roo.applyIf(attr, this.baseAttrs);
18385         }
18386         if(this.applyLoader !== false){
18387             attr.loader = this;
18388         }
18389         // uiProvider = depreciated..
18390         
18391         if(typeof(attr.uiProvider) == 'string'){
18392            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18393                 /**  eval:var:attr */ eval(attr.uiProvider);
18394         }
18395         if(typeof(this.uiProviders['default']) != 'undefined') {
18396             attr.uiProvider = this.uiProviders['default'];
18397         }
18398         
18399         this.fireEvent('create', this, attr);
18400         
18401         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18402         return(attr.leaf ?
18403                         new Roo.tree.TreeNode(attr) :
18404                         new Roo.tree.AsyncTreeNode(attr));
18405     },
18406
18407     processResponse : function(response, node, callback){
18408         var json = response.responseText;
18409         try {
18410             
18411             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
18412             if (this.root !== false) {
18413                 o = o[this.root];
18414             }
18415             
18416             for(var i = 0, len = o.length; i < len; i++){
18417                 var n = this.createNode(o[i]);
18418                 if(n){
18419                     node.appendChild(n);
18420                 }
18421             }
18422             if(typeof callback == "function"){
18423                 callback(this, node);
18424             }
18425         }catch(e){
18426             this.handleFailure(response);
18427         }
18428     },
18429
18430     handleResponse : function(response){
18431         this.transId = false;
18432         var a = response.argument;
18433         this.processResponse(response, a.node, a.callback);
18434         this.fireEvent("load", this, a.node, response);
18435     },
18436
18437     handleFailure : function(response){
18438         this.transId = false;
18439         var a = response.argument;
18440         this.fireEvent("loadexception", this, a.node, response);
18441         if(typeof a.callback == "function"){
18442             a.callback(this, a.node);
18443         }
18444     }
18445 });/*
18446  * Based on:
18447  * Ext JS Library 1.1.1
18448  * Copyright(c) 2006-2007, Ext JS, LLC.
18449  *
18450  * Originally Released Under LGPL - original licence link has changed is not relivant.
18451  *
18452  * Fork - LGPL
18453  * <script type="text/javascript">
18454  */
18455
18456 /**
18457 * @class Roo.tree.TreeFilter
18458 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18459 * @param {TreePanel} tree
18460 * @param {Object} config (optional)
18461  */
18462 Roo.tree.TreeFilter = function(tree, config){
18463     this.tree = tree;
18464     this.filtered = {};
18465     Roo.apply(this, config);
18466 };
18467
18468 Roo.tree.TreeFilter.prototype = {
18469     clearBlank:false,
18470     reverse:false,
18471     autoClear:false,
18472     remove:false,
18473
18474      /**
18475      * Filter the data by a specific attribute.
18476      * @param {String/RegExp} value Either string that the attribute value
18477      * should start with or a RegExp to test against the attribute
18478      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18479      * @param {TreeNode} startNode (optional) The node to start the filter at.
18480      */
18481     filter : function(value, attr, startNode){
18482         attr = attr || "text";
18483         var f;
18484         if(typeof value == "string"){
18485             var vlen = value.length;
18486             // auto clear empty filter
18487             if(vlen == 0 && this.clearBlank){
18488                 this.clear();
18489                 return;
18490             }
18491             value = value.toLowerCase();
18492             f = function(n){
18493                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18494             };
18495         }else if(value.exec){ // regex?
18496             f = function(n){
18497                 return value.test(n.attributes[attr]);
18498             };
18499         }else{
18500             throw 'Illegal filter type, must be string or regex';
18501         }
18502         this.filterBy(f, null, startNode);
18503         },
18504
18505     /**
18506      * Filter by a function. The passed function will be called with each
18507      * node in the tree (or from the startNode). If the function returns true, the node is kept
18508      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18509      * @param {Function} fn The filter function
18510      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18511      */
18512     filterBy : function(fn, scope, startNode){
18513         startNode = startNode || this.tree.root;
18514         if(this.autoClear){
18515             this.clear();
18516         }
18517         var af = this.filtered, rv = this.reverse;
18518         var f = function(n){
18519             if(n == startNode){
18520                 return true;
18521             }
18522             if(af[n.id]){
18523                 return false;
18524             }
18525             var m = fn.call(scope || n, n);
18526             if(!m || rv){
18527                 af[n.id] = n;
18528                 n.ui.hide();
18529                 return false;
18530             }
18531             return true;
18532         };
18533         startNode.cascade(f);
18534         if(this.remove){
18535            for(var id in af){
18536                if(typeof id != "function"){
18537                    var n = af[id];
18538                    if(n && n.parentNode){
18539                        n.parentNode.removeChild(n);
18540                    }
18541                }
18542            }
18543         }
18544     },
18545
18546     /**
18547      * Clears the current filter. Note: with the "remove" option
18548      * set a filter cannot be cleared.
18549      */
18550     clear : function(){
18551         var t = this.tree;
18552         var af = this.filtered;
18553         for(var id in af){
18554             if(typeof id != "function"){
18555                 var n = af[id];
18556                 if(n){
18557                     n.ui.show();
18558                 }
18559             }
18560         }
18561         this.filtered = {};
18562     }
18563 };
18564 /*
18565  * Based on:
18566  * Ext JS Library 1.1.1
18567  * Copyright(c) 2006-2007, Ext JS, LLC.
18568  *
18569  * Originally Released Under LGPL - original licence link has changed is not relivant.
18570  *
18571  * Fork - LGPL
18572  * <script type="text/javascript">
18573  */
18574  
18575
18576 /**
18577  * @class Roo.tree.TreeSorter
18578  * Provides sorting of nodes in a TreePanel
18579  * 
18580  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18581  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18582  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18583  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18584  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18585  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18586  * @constructor
18587  * @param {TreePanel} tree
18588  * @param {Object} config
18589  */
18590 Roo.tree.TreeSorter = function(tree, config){
18591     Roo.apply(this, config);
18592     tree.on("beforechildrenrendered", this.doSort, this);
18593     tree.on("append", this.updateSort, this);
18594     tree.on("insert", this.updateSort, this);
18595     
18596     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18597     var p = this.property || "text";
18598     var sortType = this.sortType;
18599     var fs = this.folderSort;
18600     var cs = this.caseSensitive === true;
18601     var leafAttr = this.leafAttr || 'leaf';
18602
18603     this.sortFn = function(n1, n2){
18604         if(fs){
18605             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18606                 return 1;
18607             }
18608             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18609                 return -1;
18610             }
18611         }
18612         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18613         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18614         if(v1 < v2){
18615                         return dsc ? +1 : -1;
18616                 }else if(v1 > v2){
18617                         return dsc ? -1 : +1;
18618         }else{
18619                 return 0;
18620         }
18621     };
18622 };
18623
18624 Roo.tree.TreeSorter.prototype = {
18625     doSort : function(node){
18626         node.sort(this.sortFn);
18627     },
18628     
18629     compareNodes : function(n1, n2){
18630         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18631     },
18632     
18633     updateSort : function(tree, node){
18634         if(node.childrenRendered){
18635             this.doSort.defer(1, this, [node]);
18636         }
18637     }
18638 };/*
18639  * Based on:
18640  * Ext JS Library 1.1.1
18641  * Copyright(c) 2006-2007, Ext JS, LLC.
18642  *
18643  * Originally Released Under LGPL - original licence link has changed is not relivant.
18644  *
18645  * Fork - LGPL
18646  * <script type="text/javascript">
18647  */
18648
18649 if(Roo.dd.DropZone){
18650     
18651 Roo.tree.TreeDropZone = function(tree, config){
18652     this.allowParentInsert = false;
18653     this.allowContainerDrop = false;
18654     this.appendOnly = false;
18655     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18656     this.tree = tree;
18657     this.lastInsertClass = "x-tree-no-status";
18658     this.dragOverData = {};
18659 };
18660
18661 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18662     ddGroup : "TreeDD",
18663     
18664     expandDelay : 1000,
18665     
18666     expandNode : function(node){
18667         if(node.hasChildNodes() && !node.isExpanded()){
18668             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18669         }
18670     },
18671     
18672     queueExpand : function(node){
18673         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18674     },
18675     
18676     cancelExpand : function(){
18677         if(this.expandProcId){
18678             clearTimeout(this.expandProcId);
18679             this.expandProcId = false;
18680         }
18681     },
18682     
18683     isValidDropPoint : function(n, pt, dd, e, data){
18684         if(!n || !data){ return false; }
18685         var targetNode = n.node;
18686         var dropNode = data.node;
18687         // default drop rules
18688         if(!(targetNode && targetNode.isTarget && pt)){
18689             return false;
18690         }
18691         if(pt == "append" && targetNode.allowChildren === false){
18692             return false;
18693         }
18694         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18695             return false;
18696         }
18697         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18698             return false;
18699         }
18700         // reuse the object
18701         var overEvent = this.dragOverData;
18702         overEvent.tree = this.tree;
18703         overEvent.target = targetNode;
18704         overEvent.data = data;
18705         overEvent.point = pt;
18706         overEvent.source = dd;
18707         overEvent.rawEvent = e;
18708         overEvent.dropNode = dropNode;
18709         overEvent.cancel = false;  
18710         var result = this.tree.fireEvent("nodedragover", overEvent);
18711         return overEvent.cancel === false && result !== false;
18712     },
18713     
18714     getDropPoint : function(e, n, dd){
18715         var tn = n.node;
18716         if(tn.isRoot){
18717             return tn.allowChildren !== false ? "append" : false; // always append for root
18718         }
18719         var dragEl = n.ddel;
18720         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18721         var y = Roo.lib.Event.getPageY(e);
18722         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18723         
18724         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18725         var noAppend = tn.allowChildren === false;
18726         if(this.appendOnly || tn.parentNode.allowChildren === false){
18727             return noAppend ? false : "append";
18728         }
18729         var noBelow = false;
18730         if(!this.allowParentInsert){
18731             noBelow = tn.hasChildNodes() && tn.isExpanded();
18732         }
18733         var q = (b - t) / (noAppend ? 2 : 3);
18734         if(y >= t && y < (t + q)){
18735             return "above";
18736         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18737             return "below";
18738         }else{
18739             return "append";
18740         }
18741     },
18742     
18743     onNodeEnter : function(n, dd, e, data){
18744         this.cancelExpand();
18745     },
18746     
18747     onNodeOver : function(n, dd, e, data){
18748         var pt = this.getDropPoint(e, n, dd);
18749         var node = n.node;
18750         
18751         // auto node expand check
18752         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18753             this.queueExpand(node);
18754         }else if(pt != "append"){
18755             this.cancelExpand();
18756         }
18757         
18758         // set the insert point style on the target node
18759         var returnCls = this.dropNotAllowed;
18760         if(this.isValidDropPoint(n, pt, dd, e, data)){
18761            if(pt){
18762                var el = n.ddel;
18763                var cls;
18764                if(pt == "above"){
18765                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18766                    cls = "x-tree-drag-insert-above";
18767                }else if(pt == "below"){
18768                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18769                    cls = "x-tree-drag-insert-below";
18770                }else{
18771                    returnCls = "x-tree-drop-ok-append";
18772                    cls = "x-tree-drag-append";
18773                }
18774                if(this.lastInsertClass != cls){
18775                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18776                    this.lastInsertClass = cls;
18777                }
18778            }
18779        }
18780        return returnCls;
18781     },
18782     
18783     onNodeOut : function(n, dd, e, data){
18784         this.cancelExpand();
18785         this.removeDropIndicators(n);
18786     },
18787     
18788     onNodeDrop : function(n, dd, e, data){
18789         var point = this.getDropPoint(e, n, dd);
18790         var targetNode = n.node;
18791         targetNode.ui.startDrop();
18792         if(!this.isValidDropPoint(n, point, dd, e, data)){
18793             targetNode.ui.endDrop();
18794             return false;
18795         }
18796         // first try to find the drop node
18797         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
18798         var dropEvent = {
18799             tree : this.tree,
18800             target: targetNode,
18801             data: data,
18802             point: point,
18803             source: dd,
18804             rawEvent: e,
18805             dropNode: dropNode,
18806             cancel: !dropNode   
18807         };
18808         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
18809         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
18810             targetNode.ui.endDrop();
18811             return false;
18812         }
18813         // allow target changing
18814         targetNode = dropEvent.target;
18815         if(point == "append" && !targetNode.isExpanded()){
18816             targetNode.expand(false, null, function(){
18817                 this.completeDrop(dropEvent);
18818             }.createDelegate(this));
18819         }else{
18820             this.completeDrop(dropEvent);
18821         }
18822         return true;
18823     },
18824     
18825     completeDrop : function(de){
18826         var ns = de.dropNode, p = de.point, t = de.target;
18827         if(!(ns instanceof Array)){
18828             ns = [ns];
18829         }
18830         var n;
18831         for(var i = 0, len = ns.length; i < len; i++){
18832             n = ns[i];
18833             if(p == "above"){
18834                 t.parentNode.insertBefore(n, t);
18835             }else if(p == "below"){
18836                 t.parentNode.insertBefore(n, t.nextSibling);
18837             }else{
18838                 t.appendChild(n);
18839             }
18840         }
18841         n.ui.focus();
18842         if(this.tree.hlDrop){
18843             n.ui.highlight();
18844         }
18845         t.ui.endDrop();
18846         this.tree.fireEvent("nodedrop", de);
18847     },
18848     
18849     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
18850         if(this.tree.hlDrop){
18851             dropNode.ui.focus();
18852             dropNode.ui.highlight();
18853         }
18854         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
18855     },
18856     
18857     getTree : function(){
18858         return this.tree;
18859     },
18860     
18861     removeDropIndicators : function(n){
18862         if(n && n.ddel){
18863             var el = n.ddel;
18864             Roo.fly(el).removeClass([
18865                     "x-tree-drag-insert-above",
18866                     "x-tree-drag-insert-below",
18867                     "x-tree-drag-append"]);
18868             this.lastInsertClass = "_noclass";
18869         }
18870     },
18871     
18872     beforeDragDrop : function(target, e, id){
18873         this.cancelExpand();
18874         return true;
18875     },
18876     
18877     afterRepair : function(data){
18878         if(data && Roo.enableFx){
18879             data.node.ui.highlight();
18880         }
18881         this.hideProxy();
18882     }    
18883 });
18884
18885 }
18886 /*
18887  * Based on:
18888  * Ext JS Library 1.1.1
18889  * Copyright(c) 2006-2007, Ext JS, LLC.
18890  *
18891  * Originally Released Under LGPL - original licence link has changed is not relivant.
18892  *
18893  * Fork - LGPL
18894  * <script type="text/javascript">
18895  */
18896  
18897
18898 if(Roo.dd.DragZone){
18899 Roo.tree.TreeDragZone = function(tree, config){
18900     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
18901     this.tree = tree;
18902 };
18903
18904 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
18905     ddGroup : "TreeDD",
18906     
18907     onBeforeDrag : function(data, e){
18908         var n = data.node;
18909         return n && n.draggable && !n.disabled;
18910     },
18911     
18912     onInitDrag : function(e){
18913         var data = this.dragData;
18914         this.tree.getSelectionModel().select(data.node);
18915         this.proxy.update("");
18916         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
18917         this.tree.fireEvent("startdrag", this.tree, data.node, e);
18918     },
18919     
18920     getRepairXY : function(e, data){
18921         return data.node.ui.getDDRepairXY();
18922     },
18923     
18924     onEndDrag : function(data, e){
18925         this.tree.fireEvent("enddrag", this.tree, data.node, e);
18926     },
18927     
18928     onValidDrop : function(dd, e, id){
18929         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
18930         this.hideProxy();
18931     },
18932     
18933     beforeInvalidDrop : function(e, id){
18934         // this scrolls the original position back into view
18935         var sm = this.tree.getSelectionModel();
18936         sm.clearSelections();
18937         sm.select(this.dragData.node);
18938     }
18939 });
18940 }/*
18941  * Based on:
18942  * Ext JS Library 1.1.1
18943  * Copyright(c) 2006-2007, Ext JS, LLC.
18944  *
18945  * Originally Released Under LGPL - original licence link has changed is not relivant.
18946  *
18947  * Fork - LGPL
18948  * <script type="text/javascript">
18949  */
18950 /**
18951  * @class Roo.tree.TreeEditor
18952  * @extends Roo.Editor
18953  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
18954  * as the editor field.
18955  * @constructor
18956  * @param {TreePanel} tree
18957  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
18958  */
18959 Roo.tree.TreeEditor = function(tree, config){
18960     config = config || {};
18961     var field = config.events ? config : new Roo.form.TextField(config);
18962     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
18963
18964     this.tree = tree;
18965
18966     tree.on('beforeclick', this.beforeNodeClick, this);
18967     tree.getTreeEl().on('mousedown', this.hide, this);
18968     this.on('complete', this.updateNode, this);
18969     this.on('beforestartedit', this.fitToTree, this);
18970     this.on('startedit', this.bindScroll, this, {delay:10});
18971     this.on('specialkey', this.onSpecialKey, this);
18972 };
18973
18974 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
18975     /**
18976      * @cfg {String} alignment
18977      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
18978      */
18979     alignment: "l-l",
18980     // inherit
18981     autoSize: false,
18982     /**
18983      * @cfg {Boolean} hideEl
18984      * True to hide the bound element while the editor is displayed (defaults to false)
18985      */
18986     hideEl : false,
18987     /**
18988      * @cfg {String} cls
18989      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
18990      */
18991     cls: "x-small-editor x-tree-editor",
18992     /**
18993      * @cfg {Boolean} shim
18994      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
18995      */
18996     shim:false,
18997     // inherit
18998     shadow:"frame",
18999     /**
19000      * @cfg {Number} maxWidth
19001      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
19002      * the containing tree element's size, it will be automatically limited for you to the container width, taking
19003      * scroll and client offsets into account prior to each edit.
19004      */
19005     maxWidth: 250,
19006
19007     editDelay : 350,
19008
19009     // private
19010     fitToTree : function(ed, el){
19011         var td = this.tree.getTreeEl().dom, nd = el.dom;
19012         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
19013             td.scrollLeft = nd.offsetLeft;
19014         }
19015         var w = Math.min(
19016                 this.maxWidth,
19017                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
19018         this.setSize(w, '');
19019     },
19020
19021     // private
19022     triggerEdit : function(node){
19023         this.completeEdit();
19024         this.editNode = node;
19025         this.startEdit(node.ui.textNode, node.text);
19026     },
19027
19028     // private
19029     bindScroll : function(){
19030         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
19031     },
19032
19033     // private
19034     beforeNodeClick : function(node, e){
19035         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19036         this.lastClick = new Date();
19037         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19038             e.stopEvent();
19039             this.triggerEdit(node);
19040             return false;
19041         }
19042     },
19043
19044     // private
19045     updateNode : function(ed, value){
19046         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19047         this.editNode.setText(value);
19048     },
19049
19050     // private
19051     onHide : function(){
19052         Roo.tree.TreeEditor.superclass.onHide.call(this);
19053         if(this.editNode){
19054             this.editNode.ui.focus();
19055         }
19056     },
19057
19058     // private
19059     onSpecialKey : function(field, e){
19060         var k = e.getKey();
19061         if(k == e.ESC){
19062             e.stopEvent();
19063             this.cancelEdit();
19064         }else if(k == e.ENTER && !e.hasModifier()){
19065             e.stopEvent();
19066             this.completeEdit();
19067         }
19068     }
19069 });//<Script type="text/javascript">
19070 /*
19071  * Based on:
19072  * Ext JS Library 1.1.1
19073  * Copyright(c) 2006-2007, Ext JS, LLC.
19074  *
19075  * Originally Released Under LGPL - original licence link has changed is not relivant.
19076  *
19077  * Fork - LGPL
19078  * <script type="text/javascript">
19079  */
19080  
19081 /**
19082  * Not documented??? - probably should be...
19083  */
19084
19085 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19086     //focus: Roo.emptyFn, // prevent odd scrolling behavior
19087     
19088     renderElements : function(n, a, targetNode, bulkRender){
19089         //consel.log("renderElements?");
19090         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19091
19092         var t = n.getOwnerTree();
19093         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19094         
19095         var cols = t.columns;
19096         var bw = t.borderWidth;
19097         var c = cols[0];
19098         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19099          var cb = typeof a.checked == "boolean";
19100         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19101         var colcls = 'x-t-' + tid + '-c0';
19102         var buf = [
19103             '<li class="x-tree-node">',
19104             
19105                 
19106                 '<div class="x-tree-node-el ', a.cls,'">',
19107                     // extran...
19108                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19109                 
19110                 
19111                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19112                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
19113                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19114                            (a.icon ? ' x-tree-node-inline-icon' : ''),
19115                            (a.iconCls ? ' '+a.iconCls : ''),
19116                            '" unselectable="on" />',
19117                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
19118                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
19119                              
19120                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19121                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19122                             '<span unselectable="on" qtip="' + tx + '">',
19123                              tx,
19124                              '</span></a>' ,
19125                     '</div>',
19126                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19127                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19128                  ];
19129         for(var i = 1, len = cols.length; i < len; i++){
19130             c = cols[i];
19131             colcls = 'x-t-' + tid + '-c' +i;
19132             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19133             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19134                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19135                       "</div>");
19136          }
19137          
19138          buf.push(
19139             '</a>',
19140             '<div class="x-clear"></div></div>',
19141             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19142             "</li>");
19143         
19144         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19145             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19146                                 n.nextSibling.ui.getEl(), buf.join(""));
19147         }else{
19148             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19149         }
19150         var el = this.wrap.firstChild;
19151         this.elRow = el;
19152         this.elNode = el.firstChild;
19153         this.ranchor = el.childNodes[1];
19154         this.ctNode = this.wrap.childNodes[1];
19155         var cs = el.firstChild.childNodes;
19156         this.indentNode = cs[0];
19157         this.ecNode = cs[1];
19158         this.iconNode = cs[2];
19159         var index = 3;
19160         if(cb){
19161             this.checkbox = cs[3];
19162             index++;
19163         }
19164         this.anchor = cs[index];
19165         
19166         this.textNode = cs[index].firstChild;
19167         
19168         //el.on("click", this.onClick, this);
19169         //el.on("dblclick", this.onDblClick, this);
19170         
19171         
19172        // console.log(this);
19173     },
19174     initEvents : function(){
19175         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19176         
19177             
19178         var a = this.ranchor;
19179
19180         var el = Roo.get(a);
19181
19182         if(Roo.isOpera){ // opera render bug ignores the CSS
19183             el.setStyle("text-decoration", "none");
19184         }
19185
19186         el.on("click", this.onClick, this);
19187         el.on("dblclick", this.onDblClick, this);
19188         el.on("contextmenu", this.onContextMenu, this);
19189         
19190     },
19191     
19192     /*onSelectedChange : function(state){
19193         if(state){
19194             this.focus();
19195             this.addClass("x-tree-selected");
19196         }else{
19197             //this.blur();
19198             this.removeClass("x-tree-selected");
19199         }
19200     },*/
19201     addClass : function(cls){
19202         if(this.elRow){
19203             Roo.fly(this.elRow).addClass(cls);
19204         }
19205         
19206     },
19207     
19208     
19209     removeClass : function(cls){
19210         if(this.elRow){
19211             Roo.fly(this.elRow).removeClass(cls);
19212         }
19213     }
19214
19215     
19216     
19217 });//<Script type="text/javascript">
19218
19219 /*
19220  * Based on:
19221  * Ext JS Library 1.1.1
19222  * Copyright(c) 2006-2007, Ext JS, LLC.
19223  *
19224  * Originally Released Under LGPL - original licence link has changed is not relivant.
19225  *
19226  * Fork - LGPL
19227  * <script type="text/javascript">
19228  */
19229  
19230
19231 /**
19232  * @class Roo.tree.ColumnTree
19233  * @extends Roo.data.TreePanel
19234  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19235  * @cfg {int} borderWidth  compined right/left border allowance
19236  * @constructor
19237  * @param {String/HTMLElement/Element} el The container element
19238  * @param {Object} config
19239  */
19240 Roo.tree.ColumnTree =  function(el, config)
19241 {
19242    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19243    this.addEvents({
19244         /**
19245         * @event resize
19246         * Fire this event on a container when it resizes
19247         * @param {int} w Width
19248         * @param {int} h Height
19249         */
19250        "resize" : true
19251     });
19252     this.on('resize', this.onResize, this);
19253 };
19254
19255 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19256     //lines:false,
19257     
19258     
19259     borderWidth: Roo.isBorderBox ? 0 : 2, 
19260     headEls : false,
19261     
19262     render : function(){
19263         // add the header.....
19264        
19265         Roo.tree.ColumnTree.superclass.render.apply(this);
19266         
19267         this.el.addClass('x-column-tree');
19268         
19269         this.headers = this.el.createChild(
19270             {cls:'x-tree-headers'},this.innerCt.dom);
19271    
19272         var cols = this.columns, c;
19273         var totalWidth = 0;
19274         this.headEls = [];
19275         var  len = cols.length;
19276         for(var i = 0; i < len; i++){
19277              c = cols[i];
19278              totalWidth += c.width;
19279             this.headEls.push(this.headers.createChild({
19280                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19281                  cn: {
19282                      cls:'x-tree-hd-text',
19283                      html: c.header
19284                  },
19285                  style:'width:'+(c.width-this.borderWidth)+'px;'
19286              }));
19287         }
19288         this.headers.createChild({cls:'x-clear'});
19289         // prevent floats from wrapping when clipped
19290         this.headers.setWidth(totalWidth);
19291         //this.innerCt.setWidth(totalWidth);
19292         this.innerCt.setStyle({ overflow: 'auto' });
19293         this.onResize(this.width, this.height);
19294              
19295         
19296     },
19297     onResize : function(w,h)
19298     {
19299         this.height = h;
19300         this.width = w;
19301         // resize cols..
19302         this.innerCt.setWidth(this.width);
19303         this.innerCt.setHeight(this.height-20);
19304         
19305         // headers...
19306         var cols = this.columns, c;
19307         var totalWidth = 0;
19308         var expEl = false;
19309         var len = cols.length;
19310         for(var i = 0; i < len; i++){
19311             c = cols[i];
19312             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19313                 // it's the expander..
19314                 expEl  = this.headEls[i];
19315                 continue;
19316             }
19317             totalWidth += c.width;
19318             
19319         }
19320         if (expEl) {
19321             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19322         }
19323         this.headers.setWidth(w-20);
19324
19325         
19326         
19327         
19328     }
19329 });
19330 /*
19331  * Based on:
19332  * Ext JS Library 1.1.1
19333  * Copyright(c) 2006-2007, Ext JS, LLC.
19334  *
19335  * Originally Released Under LGPL - original licence link has changed is not relivant.
19336  *
19337  * Fork - LGPL
19338  * <script type="text/javascript">
19339  */
19340  
19341 /**
19342  * @class Roo.menu.Menu
19343  * @extends Roo.util.Observable
19344  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19345  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19346  * @constructor
19347  * Creates a new Menu
19348  * @param {Object} config Configuration options
19349  */
19350 Roo.menu.Menu = function(config){
19351     Roo.apply(this, config);
19352     this.id = this.id || Roo.id();
19353     this.addEvents({
19354         /**
19355          * @event beforeshow
19356          * Fires before this menu is displayed
19357          * @param {Roo.menu.Menu} this
19358          */
19359         beforeshow : true,
19360         /**
19361          * @event beforehide
19362          * Fires before this menu is hidden
19363          * @param {Roo.menu.Menu} this
19364          */
19365         beforehide : true,
19366         /**
19367          * @event show
19368          * Fires after this menu is displayed
19369          * @param {Roo.menu.Menu} this
19370          */
19371         show : true,
19372         /**
19373          * @event hide
19374          * Fires after this menu is hidden
19375          * @param {Roo.menu.Menu} this
19376          */
19377         hide : true,
19378         /**
19379          * @event click
19380          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19381          * @param {Roo.menu.Menu} this
19382          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19383          * @param {Roo.EventObject} e
19384          */
19385         click : true,
19386         /**
19387          * @event mouseover
19388          * Fires when the mouse is hovering over this menu
19389          * @param {Roo.menu.Menu} this
19390          * @param {Roo.EventObject} e
19391          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19392          */
19393         mouseover : true,
19394         /**
19395          * @event mouseout
19396          * Fires when the mouse exits this menu
19397          * @param {Roo.menu.Menu} this
19398          * @param {Roo.EventObject} e
19399          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19400          */
19401         mouseout : true,
19402         /**
19403          * @event itemclick
19404          * Fires when a menu item contained in this menu is clicked
19405          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19406          * @param {Roo.EventObject} e
19407          */
19408         itemclick: true
19409     });
19410     if (this.registerMenu) {
19411         Roo.menu.MenuMgr.register(this);
19412     }
19413     
19414     var mis = this.items;
19415     this.items = new Roo.util.MixedCollection();
19416     if(mis){
19417         this.add.apply(this, mis);
19418     }
19419 };
19420
19421 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19422     /**
19423      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19424      */
19425     minWidth : 120,
19426     /**
19427      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19428      * for bottom-right shadow (defaults to "sides")
19429      */
19430     shadow : "sides",
19431     /**
19432      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19433      * this menu (defaults to "tl-tr?")
19434      */
19435     subMenuAlign : "tl-tr?",
19436     /**
19437      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19438      * relative to its element of origin (defaults to "tl-bl?")
19439      */
19440     defaultAlign : "tl-bl?",
19441     /**
19442      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19443      */
19444     allowOtherMenus : false,
19445     /**
19446      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19447      */
19448     registerMenu : true,
19449
19450     hidden:true,
19451
19452     // private
19453     render : function(){
19454         if(this.el){
19455             return;
19456         }
19457         var el = this.el = new Roo.Layer({
19458             cls: "x-menu",
19459             shadow:this.shadow,
19460             constrain: false,
19461             parentEl: this.parentEl || document.body,
19462             zindex:15000
19463         });
19464
19465         this.keyNav = new Roo.menu.MenuNav(this);
19466
19467         if(this.plain){
19468             el.addClass("x-menu-plain");
19469         }
19470         if(this.cls){
19471             el.addClass(this.cls);
19472         }
19473         // generic focus element
19474         this.focusEl = el.createChild({
19475             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19476         });
19477         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19478         ul.on("click", this.onClick, this);
19479         ul.on("mouseover", this.onMouseOver, this);
19480         ul.on("mouseout", this.onMouseOut, this);
19481         this.items.each(function(item){
19482             var li = document.createElement("li");
19483             li.className = "x-menu-list-item";
19484             ul.dom.appendChild(li);
19485             item.render(li, this);
19486         }, this);
19487         this.ul = ul;
19488         this.autoWidth();
19489     },
19490
19491     // private
19492     autoWidth : function(){
19493         var el = this.el, ul = this.ul;
19494         if(!el){
19495             return;
19496         }
19497         var w = this.width;
19498         if(w){
19499             el.setWidth(w);
19500         }else if(Roo.isIE){
19501             el.setWidth(this.minWidth);
19502             var t = el.dom.offsetWidth; // force recalc
19503             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19504         }
19505     },
19506
19507     // private
19508     delayAutoWidth : function(){
19509         if(this.rendered){
19510             if(!this.awTask){
19511                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19512             }
19513             this.awTask.delay(20);
19514         }
19515     },
19516
19517     // private
19518     findTargetItem : function(e){
19519         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19520         if(t && t.menuItemId){
19521             return this.items.get(t.menuItemId);
19522         }
19523     },
19524
19525     // private
19526     onClick : function(e){
19527         var t;
19528         if(t = this.findTargetItem(e)){
19529             t.onClick(e);
19530             this.fireEvent("click", this, t, e);
19531         }
19532     },
19533
19534     // private
19535     setActiveItem : function(item, autoExpand){
19536         if(item != this.activeItem){
19537             if(this.activeItem){
19538                 this.activeItem.deactivate();
19539             }
19540             this.activeItem = item;
19541             item.activate(autoExpand);
19542         }else if(autoExpand){
19543             item.expandMenu();
19544         }
19545     },
19546
19547     // private
19548     tryActivate : function(start, step){
19549         var items = this.items;
19550         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19551             var item = items.get(i);
19552             if(!item.disabled && item.canActivate){
19553                 this.setActiveItem(item, false);
19554                 return item;
19555             }
19556         }
19557         return false;
19558     },
19559
19560     // private
19561     onMouseOver : function(e){
19562         var t;
19563         if(t = this.findTargetItem(e)){
19564             if(t.canActivate && !t.disabled){
19565                 this.setActiveItem(t, true);
19566             }
19567         }
19568         this.fireEvent("mouseover", this, e, t);
19569     },
19570
19571     // private
19572     onMouseOut : function(e){
19573         var t;
19574         if(t = this.findTargetItem(e)){
19575             if(t == this.activeItem && t.shouldDeactivate(e)){
19576                 this.activeItem.deactivate();
19577                 delete this.activeItem;
19578             }
19579         }
19580         this.fireEvent("mouseout", this, e, t);
19581     },
19582
19583     /**
19584      * Read-only.  Returns true if the menu is currently displayed, else false.
19585      * @type Boolean
19586      */
19587     isVisible : function(){
19588         return this.el && !this.hidden;
19589     },
19590
19591     /**
19592      * Displays this menu relative to another element
19593      * @param {String/HTMLElement/Roo.Element} element The element to align to
19594      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19595      * the element (defaults to this.defaultAlign)
19596      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19597      */
19598     show : function(el, pos, parentMenu){
19599         this.parentMenu = parentMenu;
19600         if(!this.el){
19601             this.render();
19602         }
19603         this.fireEvent("beforeshow", this);
19604         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19605     },
19606
19607     /**
19608      * Displays this menu at a specific xy position
19609      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19610      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19611      */
19612     showAt : function(xy, parentMenu, /* private: */_e){
19613         this.parentMenu = parentMenu;
19614         if(!this.el){
19615             this.render();
19616         }
19617         if(_e !== false){
19618             this.fireEvent("beforeshow", this);
19619             xy = this.el.adjustForConstraints(xy);
19620         }
19621         this.el.setXY(xy);
19622         this.el.show();
19623         this.hidden = false;
19624         this.focus();
19625         this.fireEvent("show", this);
19626     },
19627
19628     focus : function(){
19629         if(!this.hidden){
19630             this.doFocus.defer(50, this);
19631         }
19632     },
19633
19634     doFocus : function(){
19635         if(!this.hidden){
19636             this.focusEl.focus();
19637         }
19638     },
19639
19640     /**
19641      * Hides this menu and optionally all parent menus
19642      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19643      */
19644     hide : function(deep){
19645         if(this.el && this.isVisible()){
19646             this.fireEvent("beforehide", this);
19647             if(this.activeItem){
19648                 this.activeItem.deactivate();
19649                 this.activeItem = null;
19650             }
19651             this.el.hide();
19652             this.hidden = true;
19653             this.fireEvent("hide", this);
19654         }
19655         if(deep === true && this.parentMenu){
19656             this.parentMenu.hide(true);
19657         }
19658     },
19659
19660     /**
19661      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19662      * Any of the following are valid:
19663      * <ul>
19664      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19665      * <li>An HTMLElement object which will be converted to a menu item</li>
19666      * <li>A menu item config object that will be created as a new menu item</li>
19667      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19668      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19669      * </ul>
19670      * Usage:
19671      * <pre><code>
19672 // Create the menu
19673 var menu = new Roo.menu.Menu();
19674
19675 // Create a menu item to add by reference
19676 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19677
19678 // Add a bunch of items at once using different methods.
19679 // Only the last item added will be returned.
19680 var item = menu.add(
19681     menuItem,                // add existing item by ref
19682     'Dynamic Item',          // new TextItem
19683     '-',                     // new separator
19684     { text: 'Config Item' }  // new item by config
19685 );
19686 </code></pre>
19687      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19688      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19689      */
19690     add : function(){
19691         var a = arguments, l = a.length, item;
19692         for(var i = 0; i < l; i++){
19693             var el = a[i];
19694             if ((typeof(el) == "object") && el.xtype && el.xns) {
19695                 el = Roo.factory(el, Roo.menu);
19696             }
19697             
19698             if(el.render){ // some kind of Item
19699                 item = this.addItem(el);
19700             }else if(typeof el == "string"){ // string
19701                 if(el == "separator" || el == "-"){
19702                     item = this.addSeparator();
19703                 }else{
19704                     item = this.addText(el);
19705                 }
19706             }else if(el.tagName || el.el){ // element
19707                 item = this.addElement(el);
19708             }else if(typeof el == "object"){ // must be menu item config?
19709                 item = this.addMenuItem(el);
19710             }
19711         }
19712         return item;
19713     },
19714
19715     /**
19716      * Returns this menu's underlying {@link Roo.Element} object
19717      * @return {Roo.Element} The element
19718      */
19719     getEl : function(){
19720         if(!this.el){
19721             this.render();
19722         }
19723         return this.el;
19724     },
19725
19726     /**
19727      * Adds a separator bar to the menu
19728      * @return {Roo.menu.Item} The menu item that was added
19729      */
19730     addSeparator : function(){
19731         return this.addItem(new Roo.menu.Separator());
19732     },
19733
19734     /**
19735      * Adds an {@link Roo.Element} object to the menu
19736      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19737      * @return {Roo.menu.Item} The menu item that was added
19738      */
19739     addElement : function(el){
19740         return this.addItem(new Roo.menu.BaseItem(el));
19741     },
19742
19743     /**
19744      * Adds an existing object based on {@link Roo.menu.Item} to the menu
19745      * @param {Roo.menu.Item} item The menu item to add
19746      * @return {Roo.menu.Item} The menu item that was added
19747      */
19748     addItem : function(item){
19749         this.items.add(item);
19750         if(this.ul){
19751             var li = document.createElement("li");
19752             li.className = "x-menu-list-item";
19753             this.ul.dom.appendChild(li);
19754             item.render(li, this);
19755             this.delayAutoWidth();
19756         }
19757         return item;
19758     },
19759
19760     /**
19761      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
19762      * @param {Object} config A MenuItem config object
19763      * @return {Roo.menu.Item} The menu item that was added
19764      */
19765     addMenuItem : function(config){
19766         if(!(config instanceof Roo.menu.Item)){
19767             if(typeof config.checked == "boolean"){ // must be check menu item config?
19768                 config = new Roo.menu.CheckItem(config);
19769             }else{
19770                 config = new Roo.menu.Item(config);
19771             }
19772         }
19773         return this.addItem(config);
19774     },
19775
19776     /**
19777      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
19778      * @param {String} text The text to display in the menu item
19779      * @return {Roo.menu.Item} The menu item that was added
19780      */
19781     addText : function(text){
19782         return this.addItem(new Roo.menu.TextItem({ text : text }));
19783     },
19784
19785     /**
19786      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
19787      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
19788      * @param {Roo.menu.Item} item The menu item to add
19789      * @return {Roo.menu.Item} The menu item that was added
19790      */
19791     insert : function(index, item){
19792         this.items.insert(index, item);
19793         if(this.ul){
19794             var li = document.createElement("li");
19795             li.className = "x-menu-list-item";
19796             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
19797             item.render(li, this);
19798             this.delayAutoWidth();
19799         }
19800         return item;
19801     },
19802
19803     /**
19804      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
19805      * @param {Roo.menu.Item} item The menu item to remove
19806      */
19807     remove : function(item){
19808         this.items.removeKey(item.id);
19809         item.destroy();
19810     },
19811
19812     /**
19813      * Removes and destroys all items in the menu
19814      */
19815     removeAll : function(){
19816         var f;
19817         while(f = this.items.first()){
19818             this.remove(f);
19819         }
19820     }
19821 });
19822
19823 // MenuNav is a private utility class used internally by the Menu
19824 Roo.menu.MenuNav = function(menu){
19825     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
19826     this.scope = this.menu = menu;
19827 };
19828
19829 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
19830     doRelay : function(e, h){
19831         var k = e.getKey();
19832         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
19833             this.menu.tryActivate(0, 1);
19834             return false;
19835         }
19836         return h.call(this.scope || this, e, this.menu);
19837     },
19838
19839     up : function(e, m){
19840         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
19841             m.tryActivate(m.items.length-1, -1);
19842         }
19843     },
19844
19845     down : function(e, m){
19846         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
19847             m.tryActivate(0, 1);
19848         }
19849     },
19850
19851     right : function(e, m){
19852         if(m.activeItem){
19853             m.activeItem.expandMenu(true);
19854         }
19855     },
19856
19857     left : function(e, m){
19858         m.hide();
19859         if(m.parentMenu && m.parentMenu.activeItem){
19860             m.parentMenu.activeItem.activate();
19861         }
19862     },
19863
19864     enter : function(e, m){
19865         if(m.activeItem){
19866             e.stopPropagation();
19867             m.activeItem.onClick(e);
19868             m.fireEvent("click", this, m.activeItem);
19869             return true;
19870         }
19871     }
19872 });/*
19873  * Based on:
19874  * Ext JS Library 1.1.1
19875  * Copyright(c) 2006-2007, Ext JS, LLC.
19876  *
19877  * Originally Released Under LGPL - original licence link has changed is not relivant.
19878  *
19879  * Fork - LGPL
19880  * <script type="text/javascript">
19881  */
19882  
19883 /**
19884  * @class Roo.menu.MenuMgr
19885  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
19886  * @singleton
19887  */
19888 Roo.menu.MenuMgr = function(){
19889    var menus, active, groups = {}, attached = false, lastShow = new Date();
19890
19891    // private - called when first menu is created
19892    function init(){
19893        menus = {};
19894        active = new Roo.util.MixedCollection();
19895        Roo.get(document).addKeyListener(27, function(){
19896            if(active.length > 0){
19897                hideAll();
19898            }
19899        });
19900    }
19901
19902    // private
19903    function hideAll(){
19904        if(active && active.length > 0){
19905            var c = active.clone();
19906            c.each(function(m){
19907                m.hide();
19908            });
19909        }
19910    }
19911
19912    // private
19913    function onHide(m){
19914        active.remove(m);
19915        if(active.length < 1){
19916            Roo.get(document).un("mousedown", onMouseDown);
19917            attached = false;
19918        }
19919    }
19920
19921    // private
19922    function onShow(m){
19923        var last = active.last();
19924        lastShow = new Date();
19925        active.add(m);
19926        if(!attached){
19927            Roo.get(document).on("mousedown", onMouseDown);
19928            attached = true;
19929        }
19930        if(m.parentMenu){
19931           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
19932           m.parentMenu.activeChild = m;
19933        }else if(last && last.isVisible()){
19934           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
19935        }
19936    }
19937
19938    // private
19939    function onBeforeHide(m){
19940        if(m.activeChild){
19941            m.activeChild.hide();
19942        }
19943        if(m.autoHideTimer){
19944            clearTimeout(m.autoHideTimer);
19945            delete m.autoHideTimer;
19946        }
19947    }
19948
19949    // private
19950    function onBeforeShow(m){
19951        var pm = m.parentMenu;
19952        if(!pm && !m.allowOtherMenus){
19953            hideAll();
19954        }else if(pm && pm.activeChild && active != m){
19955            pm.activeChild.hide();
19956        }
19957    }
19958
19959    // private
19960    function onMouseDown(e){
19961        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
19962            hideAll();
19963        }
19964    }
19965
19966    // private
19967    function onBeforeCheck(mi, state){
19968        if(state){
19969            var g = groups[mi.group];
19970            for(var i = 0, l = g.length; i < l; i++){
19971                if(g[i] != mi){
19972                    g[i].setChecked(false);
19973                }
19974            }
19975        }
19976    }
19977
19978    return {
19979
19980        /**
19981         * Hides all menus that are currently visible
19982         */
19983        hideAll : function(){
19984             hideAll();  
19985        },
19986
19987        // private
19988        register : function(menu){
19989            if(!menus){
19990                init();
19991            }
19992            menus[menu.id] = menu;
19993            menu.on("beforehide", onBeforeHide);
19994            menu.on("hide", onHide);
19995            menu.on("beforeshow", onBeforeShow);
19996            menu.on("show", onShow);
19997            var g = menu.group;
19998            if(g && menu.events["checkchange"]){
19999                if(!groups[g]){
20000                    groups[g] = [];
20001                }
20002                groups[g].push(menu);
20003                menu.on("checkchange", onCheck);
20004            }
20005        },
20006
20007         /**
20008          * Returns a {@link Roo.menu.Menu} object
20009          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
20010          * be used to generate and return a new Menu instance.
20011          */
20012        get : function(menu){
20013            if(typeof menu == "string"){ // menu id
20014                return menus[menu];
20015            }else if(menu.events){  // menu instance
20016                return menu;
20017            }else if(typeof menu.length == 'number'){ // array of menu items?
20018                return new Roo.menu.Menu({items:menu});
20019            }else{ // otherwise, must be a config
20020                return new Roo.menu.Menu(menu);
20021            }
20022        },
20023
20024        // private
20025        unregister : function(menu){
20026            delete menus[menu.id];
20027            menu.un("beforehide", onBeforeHide);
20028            menu.un("hide", onHide);
20029            menu.un("beforeshow", onBeforeShow);
20030            menu.un("show", onShow);
20031            var g = menu.group;
20032            if(g && menu.events["checkchange"]){
20033                groups[g].remove(menu);
20034                menu.un("checkchange", onCheck);
20035            }
20036        },
20037
20038        // private
20039        registerCheckable : function(menuItem){
20040            var g = menuItem.group;
20041            if(g){
20042                if(!groups[g]){
20043                    groups[g] = [];
20044                }
20045                groups[g].push(menuItem);
20046                menuItem.on("beforecheckchange", onBeforeCheck);
20047            }
20048        },
20049
20050        // private
20051        unregisterCheckable : function(menuItem){
20052            var g = menuItem.group;
20053            if(g){
20054                groups[g].remove(menuItem);
20055                menuItem.un("beforecheckchange", onBeforeCheck);
20056            }
20057        }
20058    };
20059 }();/*
20060  * Based on:
20061  * Ext JS Library 1.1.1
20062  * Copyright(c) 2006-2007, Ext JS, LLC.
20063  *
20064  * Originally Released Under LGPL - original licence link has changed is not relivant.
20065  *
20066  * Fork - LGPL
20067  * <script type="text/javascript">
20068  */
20069  
20070
20071 /**
20072  * @class Roo.menu.BaseItem
20073  * @extends Roo.Component
20074  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
20075  * management and base configuration options shared by all menu components.
20076  * @constructor
20077  * Creates a new BaseItem
20078  * @param {Object} config Configuration options
20079  */
20080 Roo.menu.BaseItem = function(config){
20081     Roo.menu.BaseItem.superclass.constructor.call(this, config);
20082
20083     this.addEvents({
20084         /**
20085          * @event click
20086          * Fires when this item is clicked
20087          * @param {Roo.menu.BaseItem} this
20088          * @param {Roo.EventObject} e
20089          */
20090         click: true,
20091         /**
20092          * @event activate
20093          * Fires when this item is activated
20094          * @param {Roo.menu.BaseItem} this
20095          */
20096         activate : true,
20097         /**
20098          * @event deactivate
20099          * Fires when this item is deactivated
20100          * @param {Roo.menu.BaseItem} this
20101          */
20102         deactivate : true
20103     });
20104
20105     if(this.handler){
20106         this.on("click", this.handler, this.scope, true);
20107     }
20108 };
20109
20110 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20111     /**
20112      * @cfg {Function} handler
20113      * A function that will handle the click event of this menu item (defaults to undefined)
20114      */
20115     /**
20116      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20117      */
20118     canActivate : false,
20119     /**
20120      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20121      */
20122     activeClass : "x-menu-item-active",
20123     /**
20124      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20125      */
20126     hideOnClick : true,
20127     /**
20128      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20129      */
20130     hideDelay : 100,
20131
20132     // private
20133     ctype: "Roo.menu.BaseItem",
20134
20135     // private
20136     actionMode : "container",
20137
20138     // private
20139     render : function(container, parentMenu){
20140         this.parentMenu = parentMenu;
20141         Roo.menu.BaseItem.superclass.render.call(this, container);
20142         this.container.menuItemId = this.id;
20143     },
20144
20145     // private
20146     onRender : function(container, position){
20147         this.el = Roo.get(this.el);
20148         container.dom.appendChild(this.el.dom);
20149     },
20150
20151     // private
20152     onClick : function(e){
20153         if(!this.disabled && this.fireEvent("click", this, e) !== false
20154                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20155             this.handleClick(e);
20156         }else{
20157             e.stopEvent();
20158         }
20159     },
20160
20161     // private
20162     activate : function(){
20163         if(this.disabled){
20164             return false;
20165         }
20166         var li = this.container;
20167         li.addClass(this.activeClass);
20168         this.region = li.getRegion().adjust(2, 2, -2, -2);
20169         this.fireEvent("activate", this);
20170         return true;
20171     },
20172
20173     // private
20174     deactivate : function(){
20175         this.container.removeClass(this.activeClass);
20176         this.fireEvent("deactivate", this);
20177     },
20178
20179     // private
20180     shouldDeactivate : function(e){
20181         return !this.region || !this.region.contains(e.getPoint());
20182     },
20183
20184     // private
20185     handleClick : function(e){
20186         if(this.hideOnClick){
20187             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20188         }
20189     },
20190
20191     // private
20192     expandMenu : function(autoActivate){
20193         // do nothing
20194     },
20195
20196     // private
20197     hideMenu : function(){
20198         // do nothing
20199     }
20200 });/*
20201  * Based on:
20202  * Ext JS Library 1.1.1
20203  * Copyright(c) 2006-2007, Ext JS, LLC.
20204  *
20205  * Originally Released Under LGPL - original licence link has changed is not relivant.
20206  *
20207  * Fork - LGPL
20208  * <script type="text/javascript">
20209  */
20210  
20211 /**
20212  * @class Roo.menu.Adapter
20213  * @extends Roo.menu.BaseItem
20214  * 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.
20215  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20216  * @constructor
20217  * Creates a new Adapter
20218  * @param {Object} config Configuration options
20219  */
20220 Roo.menu.Adapter = function(component, config){
20221     Roo.menu.Adapter.superclass.constructor.call(this, config);
20222     this.component = component;
20223 };
20224 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20225     // private
20226     canActivate : true,
20227
20228     // private
20229     onRender : function(container, position){
20230         this.component.render(container);
20231         this.el = this.component.getEl();
20232     },
20233
20234     // private
20235     activate : function(){
20236         if(this.disabled){
20237             return false;
20238         }
20239         this.component.focus();
20240         this.fireEvent("activate", this);
20241         return true;
20242     },
20243
20244     // private
20245     deactivate : function(){
20246         this.fireEvent("deactivate", this);
20247     },
20248
20249     // private
20250     disable : function(){
20251         this.component.disable();
20252         Roo.menu.Adapter.superclass.disable.call(this);
20253     },
20254
20255     // private
20256     enable : function(){
20257         this.component.enable();
20258         Roo.menu.Adapter.superclass.enable.call(this);
20259     }
20260 });/*
20261  * Based on:
20262  * Ext JS Library 1.1.1
20263  * Copyright(c) 2006-2007, Ext JS, LLC.
20264  *
20265  * Originally Released Under LGPL - original licence link has changed is not relivant.
20266  *
20267  * Fork - LGPL
20268  * <script type="text/javascript">
20269  */
20270
20271 /**
20272  * @class Roo.menu.TextItem
20273  * @extends Roo.menu.BaseItem
20274  * Adds a static text string to a menu, usually used as either a heading or group separator.
20275  * Note: old style constructor with text is still supported.
20276  * 
20277  * @constructor
20278  * Creates a new TextItem
20279  * @param {Object} cfg Configuration
20280  */
20281 Roo.menu.TextItem = function(cfg){
20282     if (typeof(cfg) == 'string') {
20283         this.text = cfg;
20284     } else {
20285         Roo.apply(this,cfg);
20286     }
20287     
20288     Roo.menu.TextItem.superclass.constructor.call(this);
20289 };
20290
20291 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20292     /**
20293      * @cfg {Boolean} text Text to show on item.
20294      */
20295     text : '',
20296     
20297     /**
20298      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20299      */
20300     hideOnClick : false,
20301     /**
20302      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20303      */
20304     itemCls : "x-menu-text",
20305
20306     // private
20307     onRender : function(){
20308         var s = document.createElement("span");
20309         s.className = this.itemCls;
20310         s.innerHTML = this.text;
20311         this.el = s;
20312         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20313     }
20314 });/*
20315  * Based on:
20316  * Ext JS Library 1.1.1
20317  * Copyright(c) 2006-2007, Ext JS, LLC.
20318  *
20319  * Originally Released Under LGPL - original licence link has changed is not relivant.
20320  *
20321  * Fork - LGPL
20322  * <script type="text/javascript">
20323  */
20324
20325 /**
20326  * @class Roo.menu.Separator
20327  * @extends Roo.menu.BaseItem
20328  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20329  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20330  * @constructor
20331  * @param {Object} config Configuration options
20332  */
20333 Roo.menu.Separator = function(config){
20334     Roo.menu.Separator.superclass.constructor.call(this, config);
20335 };
20336
20337 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20338     /**
20339      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20340      */
20341     itemCls : "x-menu-sep",
20342     /**
20343      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20344      */
20345     hideOnClick : false,
20346
20347     // private
20348     onRender : function(li){
20349         var s = document.createElement("span");
20350         s.className = this.itemCls;
20351         s.innerHTML = "&#160;";
20352         this.el = s;
20353         li.addClass("x-menu-sep-li");
20354         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20355     }
20356 });/*
20357  * Based on:
20358  * Ext JS Library 1.1.1
20359  * Copyright(c) 2006-2007, Ext JS, LLC.
20360  *
20361  * Originally Released Under LGPL - original licence link has changed is not relivant.
20362  *
20363  * Fork - LGPL
20364  * <script type="text/javascript">
20365  */
20366 /**
20367  * @class Roo.menu.Item
20368  * @extends Roo.menu.BaseItem
20369  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20370  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20371  * activation and click handling.
20372  * @constructor
20373  * Creates a new Item
20374  * @param {Object} config Configuration options
20375  */
20376 Roo.menu.Item = function(config){
20377     Roo.menu.Item.superclass.constructor.call(this, config);
20378     if(this.menu){
20379         this.menu = Roo.menu.MenuMgr.get(this.menu);
20380     }
20381 };
20382 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20383     
20384     /**
20385      * @cfg {String} text
20386      * The text to show on the menu item.
20387      */
20388     text: '',
20389      /**
20390      * @cfg {String} HTML to render in menu
20391      * The text to show on the menu item (HTML version).
20392      */
20393     html: '',
20394     /**
20395      * @cfg {String} icon
20396      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20397      */
20398     icon: undefined,
20399     /**
20400      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20401      */
20402     itemCls : "x-menu-item",
20403     /**
20404      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20405      */
20406     canActivate : true,
20407     /**
20408      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20409      */
20410     showDelay: 200,
20411     // doc'd in BaseItem
20412     hideDelay: 200,
20413
20414     // private
20415     ctype: "Roo.menu.Item",
20416     
20417     // private
20418     onRender : function(container, position){
20419         var el = document.createElement("a");
20420         el.hideFocus = true;
20421         el.unselectable = "on";
20422         el.href = this.href || "#";
20423         if(this.hrefTarget){
20424             el.target = this.hrefTarget;
20425         }
20426         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20427         
20428         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20429         
20430         el.innerHTML = String.format(
20431                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20432                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20433         this.el = el;
20434         Roo.menu.Item.superclass.onRender.call(this, container, position);
20435     },
20436
20437     /**
20438      * Sets the text to display in this menu item
20439      * @param {String} text The text to display
20440      * @param {Boolean} isHTML true to indicate text is pure html.
20441      */
20442     setText : function(text, isHTML){
20443         if (isHTML) {
20444             this.html = text;
20445         } else {
20446             this.text = text;
20447             this.html = '';
20448         }
20449         if(this.rendered){
20450             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20451      
20452             this.el.update(String.format(
20453                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20454                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20455             this.parentMenu.autoWidth();
20456         }
20457     },
20458
20459     // private
20460     handleClick : function(e){
20461         if(!this.href){ // if no link defined, stop the event automatically
20462             e.stopEvent();
20463         }
20464         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20465     },
20466
20467     // private
20468     activate : function(autoExpand){
20469         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20470             this.focus();
20471             if(autoExpand){
20472                 this.expandMenu();
20473             }
20474         }
20475         return true;
20476     },
20477
20478     // private
20479     shouldDeactivate : function(e){
20480         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20481             if(this.menu && this.menu.isVisible()){
20482                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20483             }
20484             return true;
20485         }
20486         return false;
20487     },
20488
20489     // private
20490     deactivate : function(){
20491         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20492         this.hideMenu();
20493     },
20494
20495     // private
20496     expandMenu : function(autoActivate){
20497         if(!this.disabled && this.menu){
20498             clearTimeout(this.hideTimer);
20499             delete this.hideTimer;
20500             if(!this.menu.isVisible() && !this.showTimer){
20501                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20502             }else if (this.menu.isVisible() && autoActivate){
20503                 this.menu.tryActivate(0, 1);
20504             }
20505         }
20506     },
20507
20508     // private
20509     deferExpand : function(autoActivate){
20510         delete this.showTimer;
20511         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20512         if(autoActivate){
20513             this.menu.tryActivate(0, 1);
20514         }
20515     },
20516
20517     // private
20518     hideMenu : function(){
20519         clearTimeout(this.showTimer);
20520         delete this.showTimer;
20521         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20522             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20523         }
20524     },
20525
20526     // private
20527     deferHide : function(){
20528         delete this.hideTimer;
20529         this.menu.hide();
20530     }
20531 });/*
20532  * Based on:
20533  * Ext JS Library 1.1.1
20534  * Copyright(c) 2006-2007, Ext JS, LLC.
20535  *
20536  * Originally Released Under LGPL - original licence link has changed is not relivant.
20537  *
20538  * Fork - LGPL
20539  * <script type="text/javascript">
20540  */
20541  
20542 /**
20543  * @class Roo.menu.CheckItem
20544  * @extends Roo.menu.Item
20545  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20546  * @constructor
20547  * Creates a new CheckItem
20548  * @param {Object} config Configuration options
20549  */
20550 Roo.menu.CheckItem = function(config){
20551     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20552     this.addEvents({
20553         /**
20554          * @event beforecheckchange
20555          * Fires before the checked value is set, providing an opportunity to cancel if needed
20556          * @param {Roo.menu.CheckItem} this
20557          * @param {Boolean} checked The new checked value that will be set
20558          */
20559         "beforecheckchange" : true,
20560         /**
20561          * @event checkchange
20562          * Fires after the checked value has been set
20563          * @param {Roo.menu.CheckItem} this
20564          * @param {Boolean} checked The checked value that was set
20565          */
20566         "checkchange" : true
20567     });
20568     if(this.checkHandler){
20569         this.on('checkchange', this.checkHandler, this.scope);
20570     }
20571 };
20572 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20573     /**
20574      * @cfg {String} group
20575      * All check items with the same group name will automatically be grouped into a single-select
20576      * radio button group (defaults to '')
20577      */
20578     /**
20579      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20580      */
20581     itemCls : "x-menu-item x-menu-check-item",
20582     /**
20583      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20584      */
20585     groupClass : "x-menu-group-item",
20586
20587     /**
20588      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20589      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20590      * initialized with checked = true will be rendered as checked.
20591      */
20592     checked: false,
20593
20594     // private
20595     ctype: "Roo.menu.CheckItem",
20596
20597     // private
20598     onRender : function(c){
20599         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20600         if(this.group){
20601             this.el.addClass(this.groupClass);
20602         }
20603         Roo.menu.MenuMgr.registerCheckable(this);
20604         if(this.checked){
20605             this.checked = false;
20606             this.setChecked(true, true);
20607         }
20608     },
20609
20610     // private
20611     destroy : function(){
20612         if(this.rendered){
20613             Roo.menu.MenuMgr.unregisterCheckable(this);
20614         }
20615         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20616     },
20617
20618     /**
20619      * Set the checked state of this item
20620      * @param {Boolean} checked The new checked value
20621      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20622      */
20623     setChecked : function(state, suppressEvent){
20624         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20625             if(this.container){
20626                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20627             }
20628             this.checked = state;
20629             if(suppressEvent !== true){
20630                 this.fireEvent("checkchange", this, state);
20631             }
20632         }
20633     },
20634
20635     // private
20636     handleClick : function(e){
20637        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20638            this.setChecked(!this.checked);
20639        }
20640        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20641     }
20642 });/*
20643  * Based on:
20644  * Ext JS Library 1.1.1
20645  * Copyright(c) 2006-2007, Ext JS, LLC.
20646  *
20647  * Originally Released Under LGPL - original licence link has changed is not relivant.
20648  *
20649  * Fork - LGPL
20650  * <script type="text/javascript">
20651  */
20652  
20653 /**
20654  * @class Roo.menu.DateItem
20655  * @extends Roo.menu.Adapter
20656  * A menu item that wraps the {@link Roo.DatPicker} component.
20657  * @constructor
20658  * Creates a new DateItem
20659  * @param {Object} config Configuration options
20660  */
20661 Roo.menu.DateItem = function(config){
20662     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20663     /** The Roo.DatePicker object @type Roo.DatePicker */
20664     this.picker = this.component;
20665     this.addEvents({select: true});
20666     
20667     this.picker.on("render", function(picker){
20668         picker.getEl().swallowEvent("click");
20669         picker.container.addClass("x-menu-date-item");
20670     });
20671
20672     this.picker.on("select", this.onSelect, this);
20673 };
20674
20675 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20676     // private
20677     onSelect : function(picker, date){
20678         this.fireEvent("select", this, date, picker);
20679         Roo.menu.DateItem.superclass.handleClick.call(this);
20680     }
20681 });/*
20682  * Based on:
20683  * Ext JS Library 1.1.1
20684  * Copyright(c) 2006-2007, Ext JS, LLC.
20685  *
20686  * Originally Released Under LGPL - original licence link has changed is not relivant.
20687  *
20688  * Fork - LGPL
20689  * <script type="text/javascript">
20690  */
20691  
20692 /**
20693  * @class Roo.menu.ColorItem
20694  * @extends Roo.menu.Adapter
20695  * A menu item that wraps the {@link Roo.ColorPalette} component.
20696  * @constructor
20697  * Creates a new ColorItem
20698  * @param {Object} config Configuration options
20699  */
20700 Roo.menu.ColorItem = function(config){
20701     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20702     /** The Roo.ColorPalette object @type Roo.ColorPalette */
20703     this.palette = this.component;
20704     this.relayEvents(this.palette, ["select"]);
20705     if(this.selectHandler){
20706         this.on('select', this.selectHandler, this.scope);
20707     }
20708 };
20709 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
20710  * Based on:
20711  * Ext JS Library 1.1.1
20712  * Copyright(c) 2006-2007, Ext JS, LLC.
20713  *
20714  * Originally Released Under LGPL - original licence link has changed is not relivant.
20715  *
20716  * Fork - LGPL
20717  * <script type="text/javascript">
20718  */
20719  
20720
20721 /**
20722  * @class Roo.menu.DateMenu
20723  * @extends Roo.menu.Menu
20724  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20725  * @constructor
20726  * Creates a new DateMenu
20727  * @param {Object} config Configuration options
20728  */
20729 Roo.menu.DateMenu = function(config){
20730     Roo.menu.DateMenu.superclass.constructor.call(this, config);
20731     this.plain = true;
20732     var di = new Roo.menu.DateItem(config);
20733     this.add(di);
20734     /**
20735      * The {@link Roo.DatePicker} instance for this DateMenu
20736      * @type DatePicker
20737      */
20738     this.picker = di.picker;
20739     /**
20740      * @event select
20741      * @param {DatePicker} picker
20742      * @param {Date} date
20743      */
20744     this.relayEvents(di, ["select"]);
20745
20746     this.on('beforeshow', function(){
20747         if(this.picker){
20748             this.picker.hideMonthPicker(true);
20749         }
20750     }, this);
20751 };
20752 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
20753     cls:'x-date-menu'
20754 });/*
20755  * Based on:
20756  * Ext JS Library 1.1.1
20757  * Copyright(c) 2006-2007, Ext JS, LLC.
20758  *
20759  * Originally Released Under LGPL - original licence link has changed is not relivant.
20760  *
20761  * Fork - LGPL
20762  * <script type="text/javascript">
20763  */
20764  
20765
20766 /**
20767  * @class Roo.menu.ColorMenu
20768  * @extends Roo.menu.Menu
20769  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
20770  * @constructor
20771  * Creates a new ColorMenu
20772  * @param {Object} config Configuration options
20773  */
20774 Roo.menu.ColorMenu = function(config){
20775     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
20776     this.plain = true;
20777     var ci = new Roo.menu.ColorItem(config);
20778     this.add(ci);
20779     /**
20780      * The {@link Roo.ColorPalette} instance for this ColorMenu
20781      * @type ColorPalette
20782      */
20783     this.palette = ci.palette;
20784     /**
20785      * @event select
20786      * @param {ColorPalette} palette
20787      * @param {String} color
20788      */
20789     this.relayEvents(ci, ["select"]);
20790 };
20791 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
20792  * Based on:
20793  * Ext JS Library 1.1.1
20794  * Copyright(c) 2006-2007, Ext JS, LLC.
20795  *
20796  * Originally Released Under LGPL - original licence link has changed is not relivant.
20797  *
20798  * Fork - LGPL
20799  * <script type="text/javascript">
20800  */
20801  
20802 /**
20803  * @class Roo.form.Field
20804  * @extends Roo.BoxComponent
20805  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
20806  * @constructor
20807  * Creates a new Field
20808  * @param {Object} config Configuration options
20809  */
20810 Roo.form.Field = function(config){
20811     Roo.form.Field.superclass.constructor.call(this, config);
20812 };
20813
20814 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
20815     /**
20816      * @cfg {String} fieldLabel Label to use when rendering a form.
20817      */
20818        /**
20819      * @cfg {String} qtip Mouse over tip
20820      */
20821      
20822     /**
20823      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
20824      */
20825     invalidClass : "x-form-invalid",
20826     /**
20827      * @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")
20828      */
20829     invalidText : "The value in this field is invalid",
20830     /**
20831      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
20832      */
20833     focusClass : "x-form-focus",
20834     /**
20835      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
20836       automatic validation (defaults to "keyup").
20837      */
20838     validationEvent : "keyup",
20839     /**
20840      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
20841      */
20842     validateOnBlur : true,
20843     /**
20844      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
20845      */
20846     validationDelay : 250,
20847     /**
20848      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20849      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
20850      */
20851     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
20852     /**
20853      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
20854      */
20855     fieldClass : "x-form-field",
20856     /**
20857      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
20858      *<pre>
20859 Value         Description
20860 -----------   ----------------------------------------------------------------------
20861 qtip          Display a quick tip when the user hovers over the field
20862 title         Display a default browser title attribute popup
20863 under         Add a block div beneath the field containing the error text
20864 side          Add an error icon to the right of the field with a popup on hover
20865 [element id]  Add the error text directly to the innerHTML of the specified element
20866 </pre>
20867      */
20868     msgTarget : 'qtip',
20869     /**
20870      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
20871      */
20872     msgFx : 'normal',
20873
20874     /**
20875      * @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.
20876      */
20877     readOnly : false,
20878
20879     /**
20880      * @cfg {Boolean} disabled True to disable the field (defaults to false).
20881      */
20882     disabled : false,
20883
20884     /**
20885      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
20886      */
20887     inputType : undefined,
20888     
20889     /**
20890      * @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).
20891          */
20892         tabIndex : undefined,
20893         
20894     // private
20895     isFormField : true,
20896
20897     // private
20898     hasFocus : false,
20899     /**
20900      * @property {Roo.Element} fieldEl
20901      * Element Containing the rendered Field (with label etc.)
20902      */
20903     /**
20904      * @cfg {Mixed} value A value to initialize this field with.
20905      */
20906     value : undefined,
20907
20908     /**
20909      * @cfg {String} name The field's HTML name attribute.
20910      */
20911     /**
20912      * @cfg {String} cls A CSS class to apply to the field's underlying element.
20913      */
20914
20915         // private ??
20916         initComponent : function(){
20917         Roo.form.Field.superclass.initComponent.call(this);
20918         this.addEvents({
20919             /**
20920              * @event focus
20921              * Fires when this field receives input focus.
20922              * @param {Roo.form.Field} this
20923              */
20924             focus : true,
20925             /**
20926              * @event blur
20927              * Fires when this field loses input focus.
20928              * @param {Roo.form.Field} this
20929              */
20930             blur : true,
20931             /**
20932              * @event specialkey
20933              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
20934              * {@link Roo.EventObject#getKey} to determine which key was pressed.
20935              * @param {Roo.form.Field} this
20936              * @param {Roo.EventObject} e The event object
20937              */
20938             specialkey : true,
20939             /**
20940              * @event change
20941              * Fires just before the field blurs if the field value has changed.
20942              * @param {Roo.form.Field} this
20943              * @param {Mixed} newValue The new value
20944              * @param {Mixed} oldValue The original value
20945              */
20946             change : true,
20947             /**
20948              * @event invalid
20949              * Fires after the field has been marked as invalid.
20950              * @param {Roo.form.Field} this
20951              * @param {String} msg The validation message
20952              */
20953             invalid : true,
20954             /**
20955              * @event valid
20956              * Fires after the field has been validated with no errors.
20957              * @param {Roo.form.Field} this
20958              */
20959             valid : true,
20960              /**
20961              * @event keyup
20962              * Fires after the key up
20963              * @param {Roo.form.Field} this
20964              * @param {Roo.EventObject}  e The event Object
20965              */
20966             keyup : true
20967         });
20968     },
20969
20970     /**
20971      * Returns the name attribute of the field if available
20972      * @return {String} name The field name
20973      */
20974     getName: function(){
20975          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
20976     },
20977
20978     // private
20979     onRender : function(ct, position){
20980         Roo.form.Field.superclass.onRender.call(this, ct, position);
20981         if(!this.el){
20982             var cfg = this.getAutoCreate();
20983             if(!cfg.name){
20984                 cfg.name = this.name || this.id;
20985             }
20986             if(this.inputType){
20987                 cfg.type = this.inputType;
20988             }
20989             this.el = ct.createChild(cfg, position);
20990         }
20991         var type = this.el.dom.type;
20992         if(type){
20993             if(type == 'password'){
20994                 type = 'text';
20995             }
20996             this.el.addClass('x-form-'+type);
20997         }
20998         if(this.readOnly){
20999             this.el.dom.readOnly = true;
21000         }
21001         if(this.tabIndex !== undefined){
21002             this.el.dom.setAttribute('tabIndex', this.tabIndex);
21003         }
21004
21005         this.el.addClass([this.fieldClass, this.cls]);
21006         this.initValue();
21007     },
21008
21009     /**
21010      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
21011      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
21012      * @return {Roo.form.Field} this
21013      */
21014     applyTo : function(target){
21015         this.allowDomMove = false;
21016         this.el = Roo.get(target);
21017         this.render(this.el.dom.parentNode);
21018         return this;
21019     },
21020
21021     // private
21022     initValue : function(){
21023         if(this.value !== undefined){
21024             this.setValue(this.value);
21025         }else if(this.el.dom.value.length > 0){
21026             this.setValue(this.el.dom.value);
21027         }
21028     },
21029
21030     /**
21031      * Returns true if this field has been changed since it was originally loaded and is not disabled.
21032      */
21033     isDirty : function() {
21034         if(this.disabled) {
21035             return false;
21036         }
21037         return String(this.getValue()) !== String(this.originalValue);
21038     },
21039
21040     // private
21041     afterRender : function(){
21042         Roo.form.Field.superclass.afterRender.call(this);
21043         this.initEvents();
21044     },
21045
21046     // private
21047     fireKey : function(e){
21048         //Roo.log('field ' + e.getKey());
21049         if(e.isNavKeyPress()){
21050             this.fireEvent("specialkey", this, e);
21051         }
21052     },
21053
21054     /**
21055      * Resets the current field value to the originally loaded value and clears any validation messages
21056      */
21057     reset : function(){
21058         this.setValue(this.originalValue);
21059         this.clearInvalid();
21060     },
21061
21062     // private
21063     initEvents : function(){
21064         // safari killled keypress - so keydown is now used..
21065         this.el.on("keydown" , this.fireKey,  this);
21066         this.el.on("focus", this.onFocus,  this);
21067         this.el.on("blur", this.onBlur,  this);
21068         this.el.relayEvent('keyup', this);
21069
21070         // reference to original value for reset
21071         this.originalValue = this.getValue();
21072     },
21073
21074     // private
21075     onFocus : function(){
21076         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21077             this.el.addClass(this.focusClass);
21078         }
21079         if(!this.hasFocus){
21080             this.hasFocus = true;
21081             this.startValue = this.getValue();
21082             this.fireEvent("focus", this);
21083         }
21084     },
21085
21086     beforeBlur : Roo.emptyFn,
21087
21088     // private
21089     onBlur : function(){
21090         this.beforeBlur();
21091         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21092             this.el.removeClass(this.focusClass);
21093         }
21094         this.hasFocus = false;
21095         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21096             this.validate();
21097         }
21098         var v = this.getValue();
21099         if(String(v) !== String(this.startValue)){
21100             this.fireEvent('change', this, v, this.startValue);
21101         }
21102         this.fireEvent("blur", this);
21103     },
21104
21105     /**
21106      * Returns whether or not the field value is currently valid
21107      * @param {Boolean} preventMark True to disable marking the field invalid
21108      * @return {Boolean} True if the value is valid, else false
21109      */
21110     isValid : function(preventMark){
21111         if(this.disabled){
21112             return true;
21113         }
21114         var restore = this.preventMark;
21115         this.preventMark = preventMark === true;
21116         var v = this.validateValue(this.processValue(this.getRawValue()));
21117         this.preventMark = restore;
21118         return v;
21119     },
21120
21121     /**
21122      * Validates the field value
21123      * @return {Boolean} True if the value is valid, else false
21124      */
21125     validate : function(){
21126         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21127             this.clearInvalid();
21128             return true;
21129         }
21130         return false;
21131     },
21132
21133     processValue : function(value){
21134         return value;
21135     },
21136
21137     // private
21138     // Subclasses should provide the validation implementation by overriding this
21139     validateValue : function(value){
21140         return true;
21141     },
21142
21143     /**
21144      * Mark this field as invalid
21145      * @param {String} msg The validation message
21146      */
21147     markInvalid : function(msg){
21148         if(!this.rendered || this.preventMark){ // not rendered
21149             return;
21150         }
21151         this.el.addClass(this.invalidClass);
21152         msg = msg || this.invalidText;
21153         switch(this.msgTarget){
21154             case 'qtip':
21155                 this.el.dom.qtip = msg;
21156                 this.el.dom.qclass = 'x-form-invalid-tip';
21157                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21158                     Roo.QuickTips.enable();
21159                 }
21160                 break;
21161             case 'title':
21162                 this.el.dom.title = msg;
21163                 break;
21164             case 'under':
21165                 if(!this.errorEl){
21166                     var elp = this.el.findParent('.x-form-element', 5, true);
21167                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21168                     this.errorEl.setWidth(elp.getWidth(true)-20);
21169                 }
21170                 this.errorEl.update(msg);
21171                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21172                 break;
21173             case 'side':
21174                 if(!this.errorIcon){
21175                     var elp = this.el.findParent('.x-form-element', 5, true);
21176                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21177                 }
21178                 this.alignErrorIcon();
21179                 this.errorIcon.dom.qtip = msg;
21180                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21181                 this.errorIcon.show();
21182                 this.on('resize', this.alignErrorIcon, this);
21183                 break;
21184             default:
21185                 var t = Roo.getDom(this.msgTarget);
21186                 t.innerHTML = msg;
21187                 t.style.display = this.msgDisplay;
21188                 break;
21189         }
21190         this.fireEvent('invalid', this, msg);
21191     },
21192
21193     // private
21194     alignErrorIcon : function(){
21195         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21196     },
21197
21198     /**
21199      * Clear any invalid styles/messages for this field
21200      */
21201     clearInvalid : function(){
21202         if(!this.rendered || this.preventMark){ // not rendered
21203             return;
21204         }
21205         this.el.removeClass(this.invalidClass);
21206         switch(this.msgTarget){
21207             case 'qtip':
21208                 this.el.dom.qtip = '';
21209                 break;
21210             case 'title':
21211                 this.el.dom.title = '';
21212                 break;
21213             case 'under':
21214                 if(this.errorEl){
21215                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21216                 }
21217                 break;
21218             case 'side':
21219                 if(this.errorIcon){
21220                     this.errorIcon.dom.qtip = '';
21221                     this.errorIcon.hide();
21222                     this.un('resize', this.alignErrorIcon, this);
21223                 }
21224                 break;
21225             default:
21226                 var t = Roo.getDom(this.msgTarget);
21227                 t.innerHTML = '';
21228                 t.style.display = 'none';
21229                 break;
21230         }
21231         this.fireEvent('valid', this);
21232     },
21233
21234     /**
21235      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21236      * @return {Mixed} value The field value
21237      */
21238     getRawValue : function(){
21239         var v = this.el.getValue();
21240         if(v === this.emptyText){
21241             v = '';
21242         }
21243         return v;
21244     },
21245
21246     /**
21247      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21248      * @return {Mixed} value The field value
21249      */
21250     getValue : function(){
21251         var v = this.el.getValue();
21252         if(v === this.emptyText || v === undefined){
21253             v = '';
21254         }
21255         return v;
21256     },
21257
21258     /**
21259      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21260      * @param {Mixed} value The value to set
21261      */
21262     setRawValue : function(v){
21263         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21264     },
21265
21266     /**
21267      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21268      * @param {Mixed} value The value to set
21269      */
21270     setValue : function(v){
21271         this.value = v;
21272         if(this.rendered){
21273             this.el.dom.value = (v === null || v === undefined ? '' : v);
21274             this.validate();
21275         }
21276     },
21277
21278     adjustSize : function(w, h){
21279         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21280         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21281         return s;
21282     },
21283
21284     adjustWidth : function(tag, w){
21285         tag = tag.toLowerCase();
21286         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21287             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21288                 if(tag == 'input'){
21289                     return w + 2;
21290                 }
21291                 if(tag = 'textarea'){
21292                     return w-2;
21293                 }
21294             }else if(Roo.isOpera){
21295                 if(tag == 'input'){
21296                     return w + 2;
21297                 }
21298                 if(tag = 'textarea'){
21299                     return w-2;
21300                 }
21301             }
21302         }
21303         return w;
21304     }
21305 });
21306
21307
21308 // anything other than normal should be considered experimental
21309 Roo.form.Field.msgFx = {
21310     normal : {
21311         show: function(msgEl, f){
21312             msgEl.setDisplayed('block');
21313         },
21314
21315         hide : function(msgEl, f){
21316             msgEl.setDisplayed(false).update('');
21317         }
21318     },
21319
21320     slide : {
21321         show: function(msgEl, f){
21322             msgEl.slideIn('t', {stopFx:true});
21323         },
21324
21325         hide : function(msgEl, f){
21326             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21327         }
21328     },
21329
21330     slideRight : {
21331         show: function(msgEl, f){
21332             msgEl.fixDisplay();
21333             msgEl.alignTo(f.el, 'tl-tr');
21334             msgEl.slideIn('l', {stopFx:true});
21335         },
21336
21337         hide : function(msgEl, f){
21338             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21339         }
21340     }
21341 };/*
21342  * Based on:
21343  * Ext JS Library 1.1.1
21344  * Copyright(c) 2006-2007, Ext JS, LLC.
21345  *
21346  * Originally Released Under LGPL - original licence link has changed is not relivant.
21347  *
21348  * Fork - LGPL
21349  * <script type="text/javascript">
21350  */
21351  
21352
21353 /**
21354  * @class Roo.form.TextField
21355  * @extends Roo.form.Field
21356  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21357  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21358  * @constructor
21359  * Creates a new TextField
21360  * @param {Object} config Configuration options
21361  */
21362 Roo.form.TextField = function(config){
21363     Roo.form.TextField.superclass.constructor.call(this, config);
21364     this.addEvents({
21365         /**
21366          * @event autosize
21367          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21368          * according to the default logic, but this event provides a hook for the developer to apply additional
21369          * logic at runtime to resize the field if needed.
21370              * @param {Roo.form.Field} this This text field
21371              * @param {Number} width The new field width
21372              */
21373         autosize : true
21374     });
21375 };
21376
21377 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21378     /**
21379      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21380      */
21381     grow : false,
21382     /**
21383      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21384      */
21385     growMin : 30,
21386     /**
21387      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21388      */
21389     growMax : 800,
21390     /**
21391      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21392      */
21393     vtype : null,
21394     /**
21395      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21396      */
21397     maskRe : null,
21398     /**
21399      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21400      */
21401     disableKeyFilter : false,
21402     /**
21403      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21404      */
21405     allowBlank : true,
21406     /**
21407      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21408      */
21409     minLength : 0,
21410     /**
21411      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21412      */
21413     maxLength : Number.MAX_VALUE,
21414     /**
21415      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21416      */
21417     minLengthText : "The minimum length for this field is {0}",
21418     /**
21419      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21420      */
21421     maxLengthText : "The maximum length for this field is {0}",
21422     /**
21423      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21424      */
21425     selectOnFocus : false,
21426     /**
21427      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21428      */
21429     blankText : "This field is required",
21430     /**
21431      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21432      * If available, this function will be called only after the basic validators all return true, and will be passed the
21433      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21434      */
21435     validator : null,
21436     /**
21437      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21438      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21439      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21440      */
21441     regex : null,
21442     /**
21443      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21444      */
21445     regexText : "",
21446     /**
21447      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
21448      */
21449     emptyText : null,
21450     /**
21451      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
21452      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
21453      */
21454     emptyClass : 'x-form-empty-field',
21455
21456     // private
21457     initEvents : function(){
21458         Roo.form.TextField.superclass.initEvents.call(this);
21459         if(this.validationEvent == 'keyup'){
21460             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21461             this.el.on('keyup', this.filterValidation, this);
21462         }
21463         else if(this.validationEvent !== false){
21464             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21465         }
21466         if(this.selectOnFocus || this.emptyText){
21467             this.on("focus", this.preFocus, this);
21468             if(this.emptyText){
21469                 this.on('blur', this.postBlur, this);
21470                 this.applyEmptyText();
21471             }
21472         }
21473         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21474             this.el.on("keypress", this.filterKeys, this);
21475         }
21476         if(this.grow){
21477             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21478             this.el.on("click", this.autoSize,  this);
21479         }
21480     },
21481
21482     processValue : function(value){
21483         if(this.stripCharsRe){
21484             var newValue = value.replace(this.stripCharsRe, '');
21485             if(newValue !== value){
21486                 this.setRawValue(newValue);
21487                 return newValue;
21488             }
21489         }
21490         return value;
21491     },
21492
21493     filterValidation : function(e){
21494         if(!e.isNavKeyPress()){
21495             this.validationTask.delay(this.validationDelay);
21496         }
21497     },
21498
21499     // private
21500     onKeyUp : function(e){
21501         if(!e.isNavKeyPress()){
21502             this.autoSize();
21503         }
21504     },
21505
21506     /**
21507      * Resets the current field value to the originally-loaded value and clears any validation messages.
21508      * Also adds emptyText and emptyClass if the original value was blank.
21509      */
21510     reset : function(){
21511         Roo.form.TextField.superclass.reset.call(this);
21512         this.applyEmptyText();
21513     },
21514
21515     applyEmptyText : function(){
21516         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
21517             this.setRawValue(this.emptyText);
21518             this.el.addClass(this.emptyClass);
21519         }
21520     },
21521
21522     // private
21523     preFocus : function(){
21524         if(this.emptyText){
21525             if(this.el.dom.value == this.emptyText){
21526                 this.setRawValue('');
21527             }
21528             this.el.removeClass(this.emptyClass);
21529         }
21530         if(this.selectOnFocus){
21531             this.el.dom.select();
21532         }
21533     },
21534
21535     // private
21536     postBlur : function(){
21537         this.applyEmptyText();
21538     },
21539
21540     // private
21541     filterKeys : function(e){
21542         var k = e.getKey();
21543         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21544             return;
21545         }
21546         var c = e.getCharCode(), cc = String.fromCharCode(c);
21547         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21548             return;
21549         }
21550         if(!this.maskRe.test(cc)){
21551             e.stopEvent();
21552         }
21553     },
21554
21555     setValue : function(v){
21556         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
21557             this.el.removeClass(this.emptyClass);
21558         }
21559         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21560         this.applyEmptyText();
21561         this.autoSize();
21562     },
21563
21564     /**
21565      * Validates a value according to the field's validation rules and marks the field as invalid
21566      * if the validation fails
21567      * @param {Mixed} value The value to validate
21568      * @return {Boolean} True if the value is valid, else false
21569      */
21570     validateValue : function(value){
21571         if(value.length < 1 || value === this.emptyText){ // if it's blank
21572              if(this.allowBlank){
21573                 this.clearInvalid();
21574                 return true;
21575              }else{
21576                 this.markInvalid(this.blankText);
21577                 return false;
21578              }
21579         }
21580         if(value.length < this.minLength){
21581             this.markInvalid(String.format(this.minLengthText, this.minLength));
21582             return false;
21583         }
21584         if(value.length > this.maxLength){
21585             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21586             return false;
21587         }
21588         if(this.vtype){
21589             var vt = Roo.form.VTypes;
21590             if(!vt[this.vtype](value, this)){
21591                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21592                 return false;
21593             }
21594         }
21595         if(typeof this.validator == "function"){
21596             var msg = this.validator(value);
21597             if(msg !== true){
21598                 this.markInvalid(msg);
21599                 return false;
21600             }
21601         }
21602         if(this.regex && !this.regex.test(value)){
21603             this.markInvalid(this.regexText);
21604             return false;
21605         }
21606         return true;
21607     },
21608
21609     /**
21610      * Selects text in this field
21611      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21612      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21613      */
21614     selectText : function(start, end){
21615         var v = this.getRawValue();
21616         if(v.length > 0){
21617             start = start === undefined ? 0 : start;
21618             end = end === undefined ? v.length : end;
21619             var d = this.el.dom;
21620             if(d.setSelectionRange){
21621                 d.setSelectionRange(start, end);
21622             }else if(d.createTextRange){
21623                 var range = d.createTextRange();
21624                 range.moveStart("character", start);
21625                 range.moveEnd("character", v.length-end);
21626                 range.select();
21627             }
21628         }
21629     },
21630
21631     /**
21632      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21633      * This only takes effect if grow = true, and fires the autosize event.
21634      */
21635     autoSize : function(){
21636         if(!this.grow || !this.rendered){
21637             return;
21638         }
21639         if(!this.metrics){
21640             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21641         }
21642         var el = this.el;
21643         var v = el.dom.value;
21644         var d = document.createElement('div');
21645         d.appendChild(document.createTextNode(v));
21646         v = d.innerHTML;
21647         d = null;
21648         v += "&#160;";
21649         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21650         this.el.setWidth(w);
21651         this.fireEvent("autosize", this, w);
21652     }
21653 });/*
21654  * Based on:
21655  * Ext JS Library 1.1.1
21656  * Copyright(c) 2006-2007, Ext JS, LLC.
21657  *
21658  * Originally Released Under LGPL - original licence link has changed is not relivant.
21659  *
21660  * Fork - LGPL
21661  * <script type="text/javascript">
21662  */
21663  
21664 /**
21665  * @class Roo.form.Hidden
21666  * @extends Roo.form.TextField
21667  * Simple Hidden element used on forms 
21668  * 
21669  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21670  * 
21671  * @constructor
21672  * Creates a new Hidden form element.
21673  * @param {Object} config Configuration options
21674  */
21675
21676
21677
21678 // easy hidden field...
21679 Roo.form.Hidden = function(config){
21680     Roo.form.Hidden.superclass.constructor.call(this, config);
21681 };
21682   
21683 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21684     fieldLabel:      '',
21685     inputType:      'hidden',
21686     width:          50,
21687     allowBlank:     true,
21688     labelSeparator: '',
21689     hidden:         true,
21690     itemCls :       'x-form-item-display-none'
21691
21692
21693 });
21694
21695
21696 /*
21697  * Based on:
21698  * Ext JS Library 1.1.1
21699  * Copyright(c) 2006-2007, Ext JS, LLC.
21700  *
21701  * Originally Released Under LGPL - original licence link has changed is not relivant.
21702  *
21703  * Fork - LGPL
21704  * <script type="text/javascript">
21705  */
21706  
21707 /**
21708  * @class Roo.form.TriggerField
21709  * @extends Roo.form.TextField
21710  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
21711  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
21712  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
21713  * for which you can provide a custom implementation.  For example:
21714  * <pre><code>
21715 var trigger = new Roo.form.TriggerField();
21716 trigger.onTriggerClick = myTriggerFn;
21717 trigger.applyTo('my-field');
21718 </code></pre>
21719  *
21720  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
21721  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
21722  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
21723  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
21724  * @constructor
21725  * Create a new TriggerField.
21726  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
21727  * to the base TextField)
21728  */
21729 Roo.form.TriggerField = function(config){
21730     this.mimicing = false;
21731     Roo.form.TriggerField.superclass.constructor.call(this, config);
21732 };
21733
21734 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
21735     /**
21736      * @cfg {String} triggerClass A CSS class to apply to the trigger
21737      */
21738     /**
21739      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21740      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
21741      */
21742     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
21743     /**
21744      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
21745      */
21746     hideTrigger:false,
21747
21748     /** @cfg {Boolean} grow @hide */
21749     /** @cfg {Number} growMin @hide */
21750     /** @cfg {Number} growMax @hide */
21751
21752     /**
21753      * @hide 
21754      * @method
21755      */
21756     autoSize: Roo.emptyFn,
21757     // private
21758     monitorTab : true,
21759     // private
21760     deferHeight : true,
21761
21762     
21763     actionMode : 'wrap',
21764     // private
21765     onResize : function(w, h){
21766         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
21767         if(typeof w == 'number'){
21768             var x = w - this.trigger.getWidth();
21769             this.el.setWidth(this.adjustWidth('input', x));
21770             this.trigger.setStyle('left', x+'px');
21771         }
21772     },
21773
21774     // private
21775     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21776
21777     // private
21778     getResizeEl : function(){
21779         return this.wrap;
21780     },
21781
21782     // private
21783     getPositionEl : function(){
21784         return this.wrap;
21785     },
21786
21787     // private
21788     alignErrorIcon : function(){
21789         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
21790     },
21791
21792     // private
21793     onRender : function(ct, position){
21794         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
21795         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
21796         this.trigger = this.wrap.createChild(this.triggerConfig ||
21797                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
21798         if(this.hideTrigger){
21799             this.trigger.setDisplayed(false);
21800         }
21801         this.initTrigger();
21802         if(!this.width){
21803             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
21804         }
21805     },
21806
21807     // private
21808     initTrigger : function(){
21809         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
21810         this.trigger.addClassOnOver('x-form-trigger-over');
21811         this.trigger.addClassOnClick('x-form-trigger-click');
21812     },
21813
21814     // private
21815     onDestroy : function(){
21816         if(this.trigger){
21817             this.trigger.removeAllListeners();
21818             this.trigger.remove();
21819         }
21820         if(this.wrap){
21821             this.wrap.remove();
21822         }
21823         Roo.form.TriggerField.superclass.onDestroy.call(this);
21824     },
21825
21826     // private
21827     onFocus : function(){
21828         Roo.form.TriggerField.superclass.onFocus.call(this);
21829         if(!this.mimicing){
21830             this.wrap.addClass('x-trigger-wrap-focus');
21831             this.mimicing = true;
21832             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
21833             if(this.monitorTab){
21834                 this.el.on("keydown", this.checkTab, this);
21835             }
21836         }
21837     },
21838
21839     // private
21840     checkTab : function(e){
21841         if(e.getKey() == e.TAB){
21842             this.triggerBlur();
21843         }
21844     },
21845
21846     // private
21847     onBlur : function(){
21848         // do nothing
21849     },
21850
21851     // private
21852     mimicBlur : function(e, t){
21853         if(!this.wrap.contains(t) && this.validateBlur()){
21854             this.triggerBlur();
21855         }
21856     },
21857
21858     // private
21859     triggerBlur : function(){
21860         this.mimicing = false;
21861         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
21862         if(this.monitorTab){
21863             this.el.un("keydown", this.checkTab, this);
21864         }
21865         this.wrap.removeClass('x-trigger-wrap-focus');
21866         Roo.form.TriggerField.superclass.onBlur.call(this);
21867     },
21868
21869     // private
21870     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
21871     validateBlur : function(e, t){
21872         return true;
21873     },
21874
21875     // private
21876     onDisable : function(){
21877         Roo.form.TriggerField.superclass.onDisable.call(this);
21878         if(this.wrap){
21879             this.wrap.addClass('x-item-disabled');
21880         }
21881     },
21882
21883     // private
21884     onEnable : function(){
21885         Roo.form.TriggerField.superclass.onEnable.call(this);
21886         if(this.wrap){
21887             this.wrap.removeClass('x-item-disabled');
21888         }
21889     },
21890
21891     // private
21892     onShow : function(){
21893         var ae = this.getActionEl();
21894         
21895         if(ae){
21896             ae.dom.style.display = '';
21897             ae.dom.style.visibility = 'visible';
21898         }
21899     },
21900
21901     // private
21902     
21903     onHide : function(){
21904         var ae = this.getActionEl();
21905         ae.dom.style.display = 'none';
21906     },
21907
21908     /**
21909      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
21910      * by an implementing function.
21911      * @method
21912      * @param {EventObject} e
21913      */
21914     onTriggerClick : Roo.emptyFn
21915 });
21916
21917 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
21918 // to be extended by an implementing class.  For an example of implementing this class, see the custom
21919 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
21920 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
21921     initComponent : function(){
21922         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
21923
21924         this.triggerConfig = {
21925             tag:'span', cls:'x-form-twin-triggers', cn:[
21926             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
21927             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
21928         ]};
21929     },
21930
21931     getTrigger : function(index){
21932         return this.triggers[index];
21933     },
21934
21935     initTrigger : function(){
21936         var ts = this.trigger.select('.x-form-trigger', true);
21937         this.wrap.setStyle('overflow', 'hidden');
21938         var triggerField = this;
21939         ts.each(function(t, all, index){
21940             t.hide = function(){
21941                 var w = triggerField.wrap.getWidth();
21942                 this.dom.style.display = 'none';
21943                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21944             };
21945             t.show = function(){
21946                 var w = triggerField.wrap.getWidth();
21947                 this.dom.style.display = '';
21948                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21949             };
21950             var triggerIndex = 'Trigger'+(index+1);
21951
21952             if(this['hide'+triggerIndex]){
21953                 t.dom.style.display = 'none';
21954             }
21955             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
21956             t.addClassOnOver('x-form-trigger-over');
21957             t.addClassOnClick('x-form-trigger-click');
21958         }, this);
21959         this.triggers = ts.elements;
21960     },
21961
21962     onTrigger1Click : Roo.emptyFn,
21963     onTrigger2Click : Roo.emptyFn
21964 });/*
21965  * Based on:
21966  * Ext JS Library 1.1.1
21967  * Copyright(c) 2006-2007, Ext JS, LLC.
21968  *
21969  * Originally Released Under LGPL - original licence link has changed is not relivant.
21970  *
21971  * Fork - LGPL
21972  * <script type="text/javascript">
21973  */
21974  
21975 /**
21976  * @class Roo.form.TextArea
21977  * @extends Roo.form.TextField
21978  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
21979  * support for auto-sizing.
21980  * @constructor
21981  * Creates a new TextArea
21982  * @param {Object} config Configuration options
21983  */
21984 Roo.form.TextArea = function(config){
21985     Roo.form.TextArea.superclass.constructor.call(this, config);
21986     // these are provided exchanges for backwards compat
21987     // minHeight/maxHeight were replaced by growMin/growMax to be
21988     // compatible with TextField growing config values
21989     if(this.minHeight !== undefined){
21990         this.growMin = this.minHeight;
21991     }
21992     if(this.maxHeight !== undefined){
21993         this.growMax = this.maxHeight;
21994     }
21995 };
21996
21997 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
21998     /**
21999      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
22000      */
22001     growMin : 60,
22002     /**
22003      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
22004      */
22005     growMax: 1000,
22006     /**
22007      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
22008      * in the field (equivalent to setting overflow: hidden, defaults to false)
22009      */
22010     preventScrollbars: false,
22011     /**
22012      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22013      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
22014      */
22015
22016     // private
22017     onRender : function(ct, position){
22018         if(!this.el){
22019             this.defaultAutoCreate = {
22020                 tag: "textarea",
22021                 style:"width:300px;height:60px;",
22022                 autocomplete: "off"
22023             };
22024         }
22025         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
22026         if(this.grow){
22027             this.textSizeEl = Roo.DomHelper.append(document.body, {
22028                 tag: "pre", cls: "x-form-grow-sizer"
22029             });
22030             if(this.preventScrollbars){
22031                 this.el.setStyle("overflow", "hidden");
22032             }
22033             this.el.setHeight(this.growMin);
22034         }
22035     },
22036
22037     onDestroy : function(){
22038         if(this.textSizeEl){
22039             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
22040         }
22041         Roo.form.TextArea.superclass.onDestroy.call(this);
22042     },
22043
22044     // private
22045     onKeyUp : function(e){
22046         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
22047             this.autoSize();
22048         }
22049     },
22050
22051     /**
22052      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
22053      * This only takes effect if grow = true, and fires the autosize event if the height changes.
22054      */
22055     autoSize : function(){
22056         if(!this.grow || !this.textSizeEl){
22057             return;
22058         }
22059         var el = this.el;
22060         var v = el.dom.value;
22061         var ts = this.textSizeEl;
22062
22063         ts.innerHTML = '';
22064         ts.appendChild(document.createTextNode(v));
22065         v = ts.innerHTML;
22066
22067         Roo.fly(ts).setWidth(this.el.getWidth());
22068         if(v.length < 1){
22069             v = "&#160;&#160;";
22070         }else{
22071             if(Roo.isIE){
22072                 v = v.replace(/\n/g, '<p>&#160;</p>');
22073             }
22074             v += "&#160;\n&#160;";
22075         }
22076         ts.innerHTML = v;
22077         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
22078         if(h != this.lastHeight){
22079             this.lastHeight = h;
22080             this.el.setHeight(h);
22081             this.fireEvent("autosize", this, h);
22082         }
22083     }
22084 });/*
22085  * Based on:
22086  * Ext JS Library 1.1.1
22087  * Copyright(c) 2006-2007, Ext JS, LLC.
22088  *
22089  * Originally Released Under LGPL - original licence link has changed is not relivant.
22090  *
22091  * Fork - LGPL
22092  * <script type="text/javascript">
22093  */
22094  
22095
22096 /**
22097  * @class Roo.form.NumberField
22098  * @extends Roo.form.TextField
22099  * Numeric text field that provides automatic keystroke filtering and numeric validation.
22100  * @constructor
22101  * Creates a new NumberField
22102  * @param {Object} config Configuration options
22103  */
22104 Roo.form.NumberField = function(config){
22105     Roo.form.NumberField.superclass.constructor.call(this, config);
22106 };
22107
22108 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
22109     /**
22110      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22111      */
22112     fieldClass: "x-form-field x-form-num-field",
22113     /**
22114      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22115      */
22116     allowDecimals : true,
22117     /**
22118      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22119      */
22120     decimalSeparator : ".",
22121     /**
22122      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22123      */
22124     decimalPrecision : 2,
22125     /**
22126      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22127      */
22128     allowNegative : true,
22129     /**
22130      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22131      */
22132     minValue : Number.NEGATIVE_INFINITY,
22133     /**
22134      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22135      */
22136     maxValue : Number.MAX_VALUE,
22137     /**
22138      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22139      */
22140     minText : "The minimum value for this field is {0}",
22141     /**
22142      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22143      */
22144     maxText : "The maximum value for this field is {0}",
22145     /**
22146      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22147      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22148      */
22149     nanText : "{0} is not a valid number",
22150
22151     // private
22152     initEvents : function(){
22153         Roo.form.NumberField.superclass.initEvents.call(this);
22154         var allowed = "0123456789";
22155         if(this.allowDecimals){
22156             allowed += this.decimalSeparator;
22157         }
22158         if(this.allowNegative){
22159             allowed += "-";
22160         }
22161         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22162         var keyPress = function(e){
22163             var k = e.getKey();
22164             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22165                 return;
22166             }
22167             var c = e.getCharCode();
22168             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22169                 e.stopEvent();
22170             }
22171         };
22172         this.el.on("keypress", keyPress, this);
22173     },
22174
22175     // private
22176     validateValue : function(value){
22177         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22178             return false;
22179         }
22180         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22181              return true;
22182         }
22183         var num = this.parseValue(value);
22184         if(isNaN(num)){
22185             this.markInvalid(String.format(this.nanText, value));
22186             return false;
22187         }
22188         if(num < this.minValue){
22189             this.markInvalid(String.format(this.minText, this.minValue));
22190             return false;
22191         }
22192         if(num > this.maxValue){
22193             this.markInvalid(String.format(this.maxText, this.maxValue));
22194             return false;
22195         }
22196         return true;
22197     },
22198
22199     getValue : function(){
22200         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22201     },
22202
22203     // private
22204     parseValue : function(value){
22205         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22206         return isNaN(value) ? '' : value;
22207     },
22208
22209     // private
22210     fixPrecision : function(value){
22211         var nan = isNaN(value);
22212         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22213             return nan ? '' : value;
22214         }
22215         return parseFloat(value).toFixed(this.decimalPrecision);
22216     },
22217
22218     setValue : function(v){
22219         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22220     },
22221
22222     // private
22223     decimalPrecisionFcn : function(v){
22224         return Math.floor(v);
22225     },
22226
22227     beforeBlur : function(){
22228         var v = this.parseValue(this.getRawValue());
22229         if(v){
22230             this.setValue(this.fixPrecision(v));
22231         }
22232     }
22233 });/*
22234  * Based on:
22235  * Ext JS Library 1.1.1
22236  * Copyright(c) 2006-2007, Ext JS, LLC.
22237  *
22238  * Originally Released Under LGPL - original licence link has changed is not relivant.
22239  *
22240  * Fork - LGPL
22241  * <script type="text/javascript">
22242  */
22243  
22244 /**
22245  * @class Roo.form.DateField
22246  * @extends Roo.form.TriggerField
22247  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22248 * @constructor
22249 * Create a new DateField
22250 * @param {Object} config
22251  */
22252 Roo.form.DateField = function(config){
22253     Roo.form.DateField.superclass.constructor.call(this, config);
22254     
22255       this.addEvents({
22256          
22257         /**
22258          * @event select
22259          * Fires when a date is selected
22260              * @param {Roo.form.DateField} combo This combo box
22261              * @param {Date} date The date selected
22262              */
22263         'select' : true
22264          
22265     });
22266     
22267     
22268     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22269     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22270     this.ddMatch = null;
22271     if(this.disabledDates){
22272         var dd = this.disabledDates;
22273         var re = "(?:";
22274         for(var i = 0; i < dd.length; i++){
22275             re += dd[i];
22276             if(i != dd.length-1) re += "|";
22277         }
22278         this.ddMatch = new RegExp(re + ")");
22279     }
22280 };
22281
22282 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22283     /**
22284      * @cfg {String} format
22285      * The default date format string which can be overriden for localization support.  The format must be
22286      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22287      */
22288     format : "m/d/y",
22289     /**
22290      * @cfg {String} altFormats
22291      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22292      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22293      */
22294     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22295     /**
22296      * @cfg {Array} disabledDays
22297      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22298      */
22299     disabledDays : null,
22300     /**
22301      * @cfg {String} disabledDaysText
22302      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22303      */
22304     disabledDaysText : "Disabled",
22305     /**
22306      * @cfg {Array} disabledDates
22307      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22308      * expression so they are very powerful. Some examples:
22309      * <ul>
22310      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22311      * <li>["03/08", "09/16"] would disable those days for every year</li>
22312      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22313      * <li>["03/../2006"] would disable every day in March 2006</li>
22314      * <li>["^03"] would disable every day in every March</li>
22315      * </ul>
22316      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22317      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22318      */
22319     disabledDates : null,
22320     /**
22321      * @cfg {String} disabledDatesText
22322      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22323      */
22324     disabledDatesText : "Disabled",
22325     /**
22326      * @cfg {Date/String} minValue
22327      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22328      * valid format (defaults to null).
22329      */
22330     minValue : null,
22331     /**
22332      * @cfg {Date/String} maxValue
22333      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22334      * valid format (defaults to null).
22335      */
22336     maxValue : null,
22337     /**
22338      * @cfg {String} minText
22339      * The error text to display when the date in the cell is before minValue (defaults to
22340      * 'The date in this field must be after {minValue}').
22341      */
22342     minText : "The date in this field must be equal to or after {0}",
22343     /**
22344      * @cfg {String} maxText
22345      * The error text to display when the date in the cell is after maxValue (defaults to
22346      * 'The date in this field must be before {maxValue}').
22347      */
22348     maxText : "The date in this field must be equal to or before {0}",
22349     /**
22350      * @cfg {String} invalidText
22351      * The error text to display when the date in the field is invalid (defaults to
22352      * '{value} is not a valid date - it must be in the format {format}').
22353      */
22354     invalidText : "{0} is not a valid date - it must be in the format {1}",
22355     /**
22356      * @cfg {String} triggerClass
22357      * An additional CSS class used to style the trigger button.  The trigger will always get the
22358      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22359      * which displays a calendar icon).
22360      */
22361     triggerClass : 'x-form-date-trigger',
22362     
22363
22364     /**
22365      * @cfg {bool} useIso
22366      * if enabled, then the date field will use a hidden field to store the 
22367      * real value as iso formated date. default (false)
22368      */ 
22369     useIso : false,
22370     /**
22371      * @cfg {String/Object} autoCreate
22372      * A DomHelper element spec, or true for a default element spec (defaults to
22373      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22374      */ 
22375     // private
22376     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22377     
22378     // private
22379     hiddenField: false,
22380     
22381     onRender : function(ct, position)
22382     {
22383         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22384         if (this.useIso) {
22385             this.el.dom.removeAttribute('name'); 
22386             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22387                     'before', true);
22388             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
22389             // prevent input submission
22390             this.hiddenName = this.name;
22391         }
22392             
22393             
22394     },
22395     
22396     // private
22397     validateValue : function(value)
22398     {
22399         value = this.formatDate(value);
22400         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22401             return false;
22402         }
22403         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22404              return true;
22405         }
22406         var svalue = value;
22407         value = this.parseDate(value);
22408         if(!value){
22409             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22410             return false;
22411         }
22412         var time = value.getTime();
22413         if(this.minValue && time < this.minValue.getTime()){
22414             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22415             return false;
22416         }
22417         if(this.maxValue && time > this.maxValue.getTime()){
22418             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22419             return false;
22420         }
22421         if(this.disabledDays){
22422             var day = value.getDay();
22423             for(var i = 0; i < this.disabledDays.length; i++) {
22424                 if(day === this.disabledDays[i]){
22425                     this.markInvalid(this.disabledDaysText);
22426                     return false;
22427                 }
22428             }
22429         }
22430         var fvalue = this.formatDate(value);
22431         if(this.ddMatch && this.ddMatch.test(fvalue)){
22432             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22433             return false;
22434         }
22435         return true;
22436     },
22437
22438     // private
22439     // Provides logic to override the default TriggerField.validateBlur which just returns true
22440     validateBlur : function(){
22441         return !this.menu || !this.menu.isVisible();
22442     },
22443
22444     /**
22445      * Returns the current date value of the date field.
22446      * @return {Date} The date value
22447      */
22448     getValue : function(){
22449         
22450         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22451     },
22452
22453     /**
22454      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22455      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22456      * (the default format used is "m/d/y").
22457      * <br />Usage:
22458      * <pre><code>
22459 //All of these calls set the same date value (May 4, 2006)
22460
22461 //Pass a date object:
22462 var dt = new Date('5/4/06');
22463 dateField.setValue(dt);
22464
22465 //Pass a date string (default format):
22466 dateField.setValue('5/4/06');
22467
22468 //Pass a date string (custom format):
22469 dateField.format = 'Y-m-d';
22470 dateField.setValue('2006-5-4');
22471 </code></pre>
22472      * @param {String/Date} date The date or valid date string
22473      */
22474     setValue : function(date){
22475         if (this.hiddenField) {
22476             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22477         }
22478         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22479     },
22480
22481     // private
22482     parseDate : function(value){
22483         if(!value || value instanceof Date){
22484             return value;
22485         }
22486         var v = Date.parseDate(value, this.format);
22487         if(!v && this.altFormats){
22488             if(!this.altFormatsArray){
22489                 this.altFormatsArray = this.altFormats.split("|");
22490             }
22491             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22492                 v = Date.parseDate(value, this.altFormatsArray[i]);
22493             }
22494         }
22495         return v;
22496     },
22497
22498     // private
22499     formatDate : function(date, fmt){
22500         return (!date || !(date instanceof Date)) ?
22501                date : date.dateFormat(fmt || this.format);
22502     },
22503
22504     // private
22505     menuListeners : {
22506         select: function(m, d){
22507             this.setValue(d);
22508             this.fireEvent('select', this, d);
22509         },
22510         show : function(){ // retain focus styling
22511             this.onFocus();
22512         },
22513         hide : function(){
22514             this.focus.defer(10, this);
22515             var ml = this.menuListeners;
22516             this.menu.un("select", ml.select,  this);
22517             this.menu.un("show", ml.show,  this);
22518             this.menu.un("hide", ml.hide,  this);
22519         }
22520     },
22521
22522     // private
22523     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22524     onTriggerClick : function(){
22525         if(this.disabled){
22526             return;
22527         }
22528         if(this.menu == null){
22529             this.menu = new Roo.menu.DateMenu();
22530         }
22531         Roo.apply(this.menu.picker,  {
22532             showClear: this.allowBlank,
22533             minDate : this.minValue,
22534             maxDate : this.maxValue,
22535             disabledDatesRE : this.ddMatch,
22536             disabledDatesText : this.disabledDatesText,
22537             disabledDays : this.disabledDays,
22538             disabledDaysText : this.disabledDaysText,
22539             format : this.format,
22540             minText : String.format(this.minText, this.formatDate(this.minValue)),
22541             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22542         });
22543         this.menu.on(Roo.apply({}, this.menuListeners, {
22544             scope:this
22545         }));
22546         this.menu.picker.setValue(this.getValue() || new Date());
22547         this.menu.show(this.el, "tl-bl?");
22548     },
22549
22550     beforeBlur : function(){
22551         var v = this.parseDate(this.getRawValue());
22552         if(v){
22553             this.setValue(v);
22554         }
22555     }
22556
22557     /** @cfg {Boolean} grow @hide */
22558     /** @cfg {Number} growMin @hide */
22559     /** @cfg {Number} growMax @hide */
22560     /**
22561      * @hide
22562      * @method autoSize
22563      */
22564 });/*
22565  * Based on:
22566  * Ext JS Library 1.1.1
22567  * Copyright(c) 2006-2007, Ext JS, LLC.
22568  *
22569  * Originally Released Under LGPL - original licence link has changed is not relivant.
22570  *
22571  * Fork - LGPL
22572  * <script type="text/javascript">
22573  */
22574  
22575
22576 /**
22577  * @class Roo.form.ComboBox
22578  * @extends Roo.form.TriggerField
22579  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22580  * @constructor
22581  * Create a new ComboBox.
22582  * @param {Object} config Configuration options
22583  */
22584 Roo.form.ComboBox = function(config){
22585     Roo.form.ComboBox.superclass.constructor.call(this, config);
22586     this.addEvents({
22587         /**
22588          * @event expand
22589          * Fires when the dropdown list is expanded
22590              * @param {Roo.form.ComboBox} combo This combo box
22591              */
22592         'expand' : true,
22593         /**
22594          * @event collapse
22595          * Fires when the dropdown list is collapsed
22596              * @param {Roo.form.ComboBox} combo This combo box
22597              */
22598         'collapse' : true,
22599         /**
22600          * @event beforeselect
22601          * Fires before a list item is selected. Return false to cancel the selection.
22602              * @param {Roo.form.ComboBox} combo This combo box
22603              * @param {Roo.data.Record} record The data record returned from the underlying store
22604              * @param {Number} index The index of the selected item in the dropdown list
22605              */
22606         'beforeselect' : true,
22607         /**
22608          * @event select
22609          * Fires when a list item is selected
22610              * @param {Roo.form.ComboBox} combo This combo box
22611              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22612              * @param {Number} index The index of the selected item in the dropdown list
22613              */
22614         'select' : true,
22615         /**
22616          * @event beforequery
22617          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22618          * The event object passed has these properties:
22619              * @param {Roo.form.ComboBox} combo This combo box
22620              * @param {String} query The query
22621              * @param {Boolean} forceAll true to force "all" query
22622              * @param {Boolean} cancel true to cancel the query
22623              * @param {Object} e The query event object
22624              */
22625         'beforequery': true,
22626          /**
22627          * @event add
22628          * Fires when the 'add' icon is pressed (add a listener to enable add button)
22629              * @param {Roo.form.ComboBox} combo This combo box
22630              */
22631         'add' : true,
22632         /**
22633          * @event edit
22634          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
22635              * @param {Roo.form.ComboBox} combo This combo box
22636              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
22637              */
22638         'edit' : true
22639         
22640         
22641     });
22642     if(this.transform){
22643         this.allowDomMove = false;
22644         var s = Roo.getDom(this.transform);
22645         if(!this.hiddenName){
22646             this.hiddenName = s.name;
22647         }
22648         if(!this.store){
22649             this.mode = 'local';
22650             var d = [], opts = s.options;
22651             for(var i = 0, len = opts.length;i < len; i++){
22652                 var o = opts[i];
22653                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22654                 if(o.selected) {
22655                     this.value = value;
22656                 }
22657                 d.push([value, o.text]);
22658             }
22659             this.store = new Roo.data.SimpleStore({
22660                 'id': 0,
22661                 fields: ['value', 'text'],
22662                 data : d
22663             });
22664             this.valueField = 'value';
22665             this.displayField = 'text';
22666         }
22667         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22668         if(!this.lazyRender){
22669             this.target = true;
22670             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22671             s.parentNode.removeChild(s); // remove it
22672             this.render(this.el.parentNode);
22673         }else{
22674             s.parentNode.removeChild(s); // remove it
22675         }
22676
22677     }
22678     if (this.store) {
22679         this.store = Roo.factory(this.store, Roo.data);
22680     }
22681     
22682     this.selectedIndex = -1;
22683     if(this.mode == 'local'){
22684         if(config.queryDelay === undefined){
22685             this.queryDelay = 10;
22686         }
22687         if(config.minChars === undefined){
22688             this.minChars = 0;
22689         }
22690     }
22691 };
22692
22693 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22694     /**
22695      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22696      */
22697     /**
22698      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
22699      * rendering into an Roo.Editor, defaults to false)
22700      */
22701     /**
22702      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
22703      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
22704      */
22705     /**
22706      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
22707      */
22708     /**
22709      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
22710      * the dropdown list (defaults to undefined, with no header element)
22711      */
22712
22713      /**
22714      * @cfg {String/Roo.Template} tpl The template to use to render the output
22715      */
22716      
22717     // private
22718     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
22719     /**
22720      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
22721      */
22722     listWidth: undefined,
22723     /**
22724      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
22725      * mode = 'remote' or 'text' if mode = 'local')
22726      */
22727     displayField: undefined,
22728     /**
22729      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
22730      * mode = 'remote' or 'value' if mode = 'local'). 
22731      * Note: use of a valueField requires the user make a selection
22732      * in order for a value to be mapped.
22733      */
22734     valueField: undefined,
22735     /**
22736      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
22737      * field's data value (defaults to the underlying DOM element's name)
22738      */
22739     hiddenName: undefined,
22740     /**
22741      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
22742      */
22743     listClass: '',
22744     /**
22745      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
22746      */
22747     selectedClass: 'x-combo-selected',
22748     /**
22749      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22750      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
22751      * which displays a downward arrow icon).
22752      */
22753     triggerClass : 'x-form-arrow-trigger',
22754     /**
22755      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
22756      */
22757     shadow:'sides',
22758     /**
22759      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
22760      * anchor positions (defaults to 'tl-bl')
22761      */
22762     listAlign: 'tl-bl?',
22763     /**
22764      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
22765      */
22766     maxHeight: 300,
22767     /**
22768      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
22769      * query specified by the allQuery config option (defaults to 'query')
22770      */
22771     triggerAction: 'query',
22772     /**
22773      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
22774      * (defaults to 4, does not apply if editable = false)
22775      */
22776     minChars : 4,
22777     /**
22778      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
22779      * delay (typeAheadDelay) if it matches a known value (defaults to false)
22780      */
22781     typeAhead: false,
22782     /**
22783      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
22784      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
22785      */
22786     queryDelay: 500,
22787     /**
22788      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
22789      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
22790      */
22791     pageSize: 0,
22792     /**
22793      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
22794      * when editable = true (defaults to false)
22795      */
22796     selectOnFocus:false,
22797     /**
22798      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
22799      */
22800     queryParam: 'query',
22801     /**
22802      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
22803      * when mode = 'remote' (defaults to 'Loading...')
22804      */
22805     loadingText: 'Loading...',
22806     /**
22807      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
22808      */
22809     resizable: false,
22810     /**
22811      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
22812      */
22813     handleHeight : 8,
22814     /**
22815      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
22816      * traditional select (defaults to true)
22817      */
22818     editable: true,
22819     /**
22820      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
22821      */
22822     allQuery: '',
22823     /**
22824      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
22825      */
22826     mode: 'remote',
22827     /**
22828      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
22829      * listWidth has a higher value)
22830      */
22831     minListWidth : 70,
22832     /**
22833      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
22834      * allow the user to set arbitrary text into the field (defaults to false)
22835      */
22836     forceSelection:false,
22837     /**
22838      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
22839      * if typeAhead = true (defaults to 250)
22840      */
22841     typeAheadDelay : 250,
22842     /**
22843      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
22844      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
22845      */
22846     valueNotFoundText : undefined,
22847     /**
22848      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
22849      */
22850     blockFocus : false,
22851     
22852     /**
22853      * @cfg {Boolean} disableClear Disable showing of clear button.
22854      */
22855     disableClear : false,
22856     /**
22857      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
22858      */
22859     alwaysQuery : false,
22860     
22861     //private
22862     addicon : false,
22863     editicon: false,
22864     
22865     
22866     // private
22867     onRender : function(ct, position){
22868         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
22869         if(this.hiddenName){
22870             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
22871                     'before', true);
22872             this.hiddenField.value =
22873                 this.hiddenValue !== undefined ? this.hiddenValue :
22874                 this.value !== undefined ? this.value : '';
22875
22876             // prevent input submission
22877             this.el.dom.removeAttribute('name');
22878         }
22879         if(Roo.isGecko){
22880             this.el.dom.setAttribute('autocomplete', 'off');
22881         }
22882
22883         var cls = 'x-combo-list';
22884
22885         this.list = new Roo.Layer({
22886             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
22887         });
22888
22889         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
22890         this.list.setWidth(lw);
22891         this.list.swallowEvent('mousewheel');
22892         this.assetHeight = 0;
22893
22894         if(this.title){
22895             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
22896             this.assetHeight += this.header.getHeight();
22897         }
22898
22899         this.innerList = this.list.createChild({cls:cls+'-inner'});
22900         this.innerList.on('mouseover', this.onViewOver, this);
22901         this.innerList.on('mousemove', this.onViewMove, this);
22902         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
22903         
22904         if(this.allowBlank && !this.pageSize && !this.disableClear){
22905             this.footer = this.list.createChild({cls:cls+'-ft'});
22906             this.pageTb = new Roo.Toolbar(this.footer);
22907            
22908         }
22909         if(this.pageSize){
22910             this.footer = this.list.createChild({cls:cls+'-ft'});
22911             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
22912                     {pageSize: this.pageSize});
22913             
22914         }
22915         
22916         if (this.pageTb && this.allowBlank && !this.disableClear) {
22917             var _this = this;
22918             this.pageTb.add(new Roo.Toolbar.Fill(), {
22919                 cls: 'x-btn-icon x-btn-clear',
22920                 text: '&#160;',
22921                 handler: function()
22922                 {
22923                     _this.collapse();
22924                     _this.clearValue();
22925                     _this.onSelect(false, -1);
22926                 }
22927             });
22928         }
22929         if (this.footer) {
22930             this.assetHeight += this.footer.getHeight();
22931         }
22932         
22933
22934         if(!this.tpl){
22935             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
22936         }
22937
22938         this.view = new Roo.View(this.innerList, this.tpl, {
22939             singleSelect:true, store: this.store, selectedClass: this.selectedClass
22940         });
22941
22942         this.view.on('click', this.onViewClick, this);
22943
22944         this.store.on('beforeload', this.onBeforeLoad, this);
22945         this.store.on('load', this.onLoad, this);
22946         this.store.on('loadexception', this.collapse, this);
22947
22948         if(this.resizable){
22949             this.resizer = new Roo.Resizable(this.list,  {
22950                pinned:true, handles:'se'
22951             });
22952             this.resizer.on('resize', function(r, w, h){
22953                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
22954                 this.listWidth = w;
22955                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
22956                 this.restrictHeight();
22957             }, this);
22958             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
22959         }
22960         if(!this.editable){
22961             this.editable = true;
22962             this.setEditable(false);
22963         }  
22964         
22965         
22966         if (typeof(this.events.add.listeners) != 'undefined') {
22967             
22968             this.addicon = this.wrap.createChild(
22969                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
22970        
22971             this.addicon.on('click', function(e) {
22972                 this.fireEvent('add', this);
22973             }, this);
22974         }
22975         if (typeof(this.events.edit.listeners) != 'undefined') {
22976             
22977             this.editicon = this.wrap.createChild(
22978                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
22979             if (this.addicon) {
22980                 this.editicon.setStyle('margin-left', '40px');
22981             }
22982             this.editicon.on('click', function(e) {
22983                 
22984                 // we fire even  if inothing is selected..
22985                 this.fireEvent('edit', this, this.lastData );
22986                 
22987             }, this);
22988         }
22989         
22990         
22991         
22992     },
22993
22994     // private
22995     initEvents : function(){
22996         Roo.form.ComboBox.superclass.initEvents.call(this);
22997
22998         this.keyNav = new Roo.KeyNav(this.el, {
22999             "up" : function(e){
23000                 this.inKeyMode = true;
23001                 this.selectPrev();
23002             },
23003
23004             "down" : function(e){
23005                 if(!this.isExpanded()){
23006                     this.onTriggerClick();
23007                 }else{
23008                     this.inKeyMode = true;
23009                     this.selectNext();
23010                 }
23011             },
23012
23013             "enter" : function(e){
23014                 this.onViewClick();
23015                 //return true;
23016             },
23017
23018             "esc" : function(e){
23019                 this.collapse();
23020             },
23021
23022             "tab" : function(e){
23023                 this.onViewClick(false);
23024                 return true;
23025             },
23026
23027             scope : this,
23028
23029             doRelay : function(foo, bar, hname){
23030                 if(hname == 'down' || this.scope.isExpanded()){
23031                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23032                 }
23033                 return true;
23034             },
23035
23036             forceKeyDown: true
23037         });
23038         this.queryDelay = Math.max(this.queryDelay || 10,
23039                 this.mode == 'local' ? 10 : 250);
23040         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23041         if(this.typeAhead){
23042             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23043         }
23044         if(this.editable !== false){
23045             this.el.on("keyup", this.onKeyUp, this);
23046         }
23047         if(this.forceSelection){
23048             this.on('blur', this.doForce, this);
23049         }
23050     },
23051
23052     onDestroy : function(){
23053         if(this.view){
23054             this.view.setStore(null);
23055             this.view.el.removeAllListeners();
23056             this.view.el.remove();
23057             this.view.purgeListeners();
23058         }
23059         if(this.list){
23060             this.list.destroy();
23061         }
23062         if(this.store){
23063             this.store.un('beforeload', this.onBeforeLoad, this);
23064             this.store.un('load', this.onLoad, this);
23065             this.store.un('loadexception', this.collapse, this);
23066         }
23067         Roo.form.ComboBox.superclass.onDestroy.call(this);
23068     },
23069
23070     // private
23071     fireKey : function(e){
23072         if(e.isNavKeyPress() && !this.list.isVisible()){
23073             this.fireEvent("specialkey", this, e);
23074         }
23075     },
23076
23077     // private
23078     onResize: function(w, h){
23079         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23080         
23081         if(typeof w != 'number'){
23082             // we do not handle it!?!?
23083             return;
23084         }
23085         var tw = this.trigger.getWidth();
23086         tw += this.addicon ? this.addicon.getWidth() : 0;
23087         tw += this.editicon ? this.editicon.getWidth() : 0;
23088         var x = w - tw;
23089         this.el.setWidth( this.adjustWidth('input', x));
23090             
23091         this.trigger.setStyle('left', x+'px');
23092         
23093         if(this.list && this.listWidth === undefined){
23094             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23095             this.list.setWidth(lw);
23096             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23097         }
23098         
23099     
23100         
23101     },
23102
23103     /**
23104      * Allow or prevent the user from directly editing the field text.  If false is passed,
23105      * the user will only be able to select from the items defined in the dropdown list.  This method
23106      * is the runtime equivalent of setting the 'editable' config option at config time.
23107      * @param {Boolean} value True to allow the user to directly edit the field text
23108      */
23109     setEditable : function(value){
23110         if(value == this.editable){
23111             return;
23112         }
23113         this.editable = value;
23114         if(!value){
23115             this.el.dom.setAttribute('readOnly', true);
23116             this.el.on('mousedown', this.onTriggerClick,  this);
23117             this.el.addClass('x-combo-noedit');
23118         }else{
23119             this.el.dom.setAttribute('readOnly', false);
23120             this.el.un('mousedown', this.onTriggerClick,  this);
23121             this.el.removeClass('x-combo-noedit');
23122         }
23123     },
23124
23125     // private
23126     onBeforeLoad : function(){
23127         if(!this.hasFocus){
23128             return;
23129         }
23130         this.innerList.update(this.loadingText ?
23131                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23132         this.restrictHeight();
23133         this.selectedIndex = -1;
23134     },
23135
23136     // private
23137     onLoad : function(){
23138         if(!this.hasFocus){
23139             return;
23140         }
23141         if(this.store.getCount() > 0){
23142             this.expand();
23143             this.restrictHeight();
23144             if(this.lastQuery == this.allQuery){
23145                 if(this.editable){
23146                     this.el.dom.select();
23147                 }
23148                 if(!this.selectByValue(this.value, true)){
23149                     this.select(0, true);
23150                 }
23151             }else{
23152                 this.selectNext();
23153                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23154                     this.taTask.delay(this.typeAheadDelay);
23155                 }
23156             }
23157         }else{
23158             this.onEmptyResults();
23159         }
23160         //this.el.focus();
23161     },
23162
23163     // private
23164     onTypeAhead : function(){
23165         if(this.store.getCount() > 0){
23166             var r = this.store.getAt(0);
23167             var newValue = r.data[this.displayField];
23168             var len = newValue.length;
23169             var selStart = this.getRawValue().length;
23170             if(selStart != len){
23171                 this.setRawValue(newValue);
23172                 this.selectText(selStart, newValue.length);
23173             }
23174         }
23175     },
23176
23177     // private
23178     onSelect : function(record, index){
23179         if(this.fireEvent('beforeselect', this, record, index) !== false){
23180             this.setFromData(index > -1 ? record.data : false);
23181             this.collapse();
23182             this.fireEvent('select', this, record, index);
23183         }
23184     },
23185
23186     /**
23187      * Returns the currently selected field value or empty string if no value is set.
23188      * @return {String} value The selected value
23189      */
23190     getValue : function(){
23191         if(this.valueField){
23192             return typeof this.value != 'undefined' ? this.value : '';
23193         }else{
23194             return Roo.form.ComboBox.superclass.getValue.call(this);
23195         }
23196     },
23197
23198     /**
23199      * Clears any text/value currently set in the field
23200      */
23201     clearValue : function(){
23202         if(this.hiddenField){
23203             this.hiddenField.value = '';
23204         }
23205         this.value = '';
23206         this.setRawValue('');
23207         this.lastSelectionText = '';
23208         this.applyEmptyText();
23209     },
23210
23211     /**
23212      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23213      * will be displayed in the field.  If the value does not match the data value of an existing item,
23214      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23215      * Otherwise the field will be blank (although the value will still be set).
23216      * @param {String} value The value to match
23217      */
23218     setValue : function(v){
23219         var text = v;
23220         if(this.valueField){
23221             var r = this.findRecord(this.valueField, v);
23222             if(r){
23223                 text = r.data[this.displayField];
23224             }else if(this.valueNotFoundText !== undefined){
23225                 text = this.valueNotFoundText;
23226             }
23227         }
23228         this.lastSelectionText = text;
23229         if(this.hiddenField){
23230             this.hiddenField.value = v;
23231         }
23232         Roo.form.ComboBox.superclass.setValue.call(this, text);
23233         this.value = v;
23234     },
23235     /**
23236      * @property {Object} the last set data for the element
23237      */
23238     
23239     lastData : false,
23240     /**
23241      * Sets the value of the field based on a object which is related to the record format for the store.
23242      * @param {Object} value the value to set as. or false on reset?
23243      */
23244     setFromData : function(o){
23245         var dv = ''; // display value
23246         var vv = ''; // value value..
23247         this.lastData = o;
23248         if (this.displayField) {
23249             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23250         } else {
23251             // this is an error condition!!!
23252             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23253         }
23254         
23255         if(this.valueField){
23256             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23257         }
23258         if(this.hiddenField){
23259             this.hiddenField.value = vv;
23260             
23261             this.lastSelectionText = dv;
23262             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23263             this.value = vv;
23264             return;
23265         }
23266         // no hidden field.. - we store the value in 'value', but still display
23267         // display field!!!!
23268         this.lastSelectionText = dv;
23269         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23270         this.value = vv;
23271         
23272         
23273     },
23274     // private
23275     reset : function(){
23276         // overridden so that last data is reset..
23277         this.setValue(this.originalValue);
23278         this.clearInvalid();
23279         this.lastData = false;
23280     },
23281     // private
23282     findRecord : function(prop, value){
23283         var record;
23284         if(this.store.getCount() > 0){
23285             this.store.each(function(r){
23286                 if(r.data[prop] == value){
23287                     record = r;
23288                     return false;
23289                 }
23290             });
23291         }
23292         return record;
23293     },
23294
23295     // private
23296     onViewMove : function(e, t){
23297         this.inKeyMode = false;
23298     },
23299
23300     // private
23301     onViewOver : function(e, t){
23302         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23303             return;
23304         }
23305         var item = this.view.findItemFromChild(t);
23306         if(item){
23307             var index = this.view.indexOf(item);
23308             this.select(index, false);
23309         }
23310     },
23311
23312     // private
23313     onViewClick : function(doFocus){
23314         var index = this.view.getSelectedIndexes()[0];
23315         var r = this.store.getAt(index);
23316         if(r){
23317             this.onSelect(r, index);
23318         }
23319         if(doFocus !== false && !this.blockFocus){
23320             this.el.focus();
23321         }
23322     },
23323
23324     // private
23325     restrictHeight : function(){
23326         this.innerList.dom.style.height = '';
23327         var inner = this.innerList.dom;
23328         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23329         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23330         this.list.beginUpdate();
23331         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23332         this.list.alignTo(this.el, this.listAlign);
23333         this.list.endUpdate();
23334     },
23335
23336     // private
23337     onEmptyResults : function(){
23338         this.collapse();
23339     },
23340
23341     /**
23342      * Returns true if the dropdown list is expanded, else false.
23343      */
23344     isExpanded : function(){
23345         return this.list.isVisible();
23346     },
23347
23348     /**
23349      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23350      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23351      * @param {String} value The data value of the item to select
23352      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23353      * selected item if it is not currently in view (defaults to true)
23354      * @return {Boolean} True if the value matched an item in the list, else false
23355      */
23356     selectByValue : function(v, scrollIntoView){
23357         if(v !== undefined && v !== null){
23358             var r = this.findRecord(this.valueField || this.displayField, v);
23359             if(r){
23360                 this.select(this.store.indexOf(r), scrollIntoView);
23361                 return true;
23362             }
23363         }
23364         return false;
23365     },
23366
23367     /**
23368      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23369      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23370      * @param {Number} index The zero-based index of the list item to select
23371      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23372      * selected item if it is not currently in view (defaults to true)
23373      */
23374     select : function(index, scrollIntoView){
23375         this.selectedIndex = index;
23376         this.view.select(index);
23377         if(scrollIntoView !== false){
23378             var el = this.view.getNode(index);
23379             if(el){
23380                 this.innerList.scrollChildIntoView(el, false);
23381             }
23382         }
23383     },
23384
23385     // private
23386     selectNext : function(){
23387         var ct = this.store.getCount();
23388         if(ct > 0){
23389             if(this.selectedIndex == -1){
23390                 this.select(0);
23391             }else if(this.selectedIndex < ct-1){
23392                 this.select(this.selectedIndex+1);
23393             }
23394         }
23395     },
23396
23397     // private
23398     selectPrev : function(){
23399         var ct = this.store.getCount();
23400         if(ct > 0){
23401             if(this.selectedIndex == -1){
23402                 this.select(0);
23403             }else if(this.selectedIndex != 0){
23404                 this.select(this.selectedIndex-1);
23405             }
23406         }
23407     },
23408
23409     // private
23410     onKeyUp : function(e){
23411         if(this.editable !== false && !e.isSpecialKey()){
23412             this.lastKey = e.getKey();
23413             this.dqTask.delay(this.queryDelay);
23414         }
23415     },
23416
23417     // private
23418     validateBlur : function(){
23419         return !this.list || !this.list.isVisible();   
23420     },
23421
23422     // private
23423     initQuery : function(){
23424         this.doQuery(this.getRawValue());
23425     },
23426
23427     // private
23428     doForce : function(){
23429         if(this.el.dom.value.length > 0){
23430             this.el.dom.value =
23431                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23432             this.applyEmptyText();
23433         }
23434     },
23435
23436     /**
23437      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23438      * query allowing the query action to be canceled if needed.
23439      * @param {String} query The SQL query to execute
23440      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23441      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23442      * saved in the current store (defaults to false)
23443      */
23444     doQuery : function(q, forceAll){
23445         if(q === undefined || q === null){
23446             q = '';
23447         }
23448         var qe = {
23449             query: q,
23450             forceAll: forceAll,
23451             combo: this,
23452             cancel:false
23453         };
23454         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23455             return false;
23456         }
23457         q = qe.query;
23458         forceAll = qe.forceAll;
23459         if(forceAll === true || (q.length >= this.minChars)){
23460             if(this.lastQuery != q || this.alwaysQuery){
23461                 this.lastQuery = q;
23462                 if(this.mode == 'local'){
23463                     this.selectedIndex = -1;
23464                     if(forceAll){
23465                         this.store.clearFilter();
23466                     }else{
23467                         this.store.filter(this.displayField, q);
23468                     }
23469                     this.onLoad();
23470                 }else{
23471                     this.store.baseParams[this.queryParam] = q;
23472                     this.store.load({
23473                         params: this.getParams(q)
23474                     });
23475                     this.expand();
23476                 }
23477             }else{
23478                 this.selectedIndex = -1;
23479                 this.onLoad();   
23480             }
23481         }
23482     },
23483
23484     // private
23485     getParams : function(q){
23486         var p = {};
23487         //p[this.queryParam] = q;
23488         if(this.pageSize){
23489             p.start = 0;
23490             p.limit = this.pageSize;
23491         }
23492         return p;
23493     },
23494
23495     /**
23496      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23497      */
23498     collapse : function(){
23499         if(!this.isExpanded()){
23500             return;
23501         }
23502         this.list.hide();
23503         Roo.get(document).un('mousedown', this.collapseIf, this);
23504         Roo.get(document).un('mousewheel', this.collapseIf, this);
23505         if (!this.editable) {
23506             Roo.get(document).un('keydown', this.listKeyPress, this);
23507         }
23508         this.fireEvent('collapse', this);
23509     },
23510
23511     // private
23512     collapseIf : function(e){
23513         if(!e.within(this.wrap) && !e.within(this.list)){
23514             this.collapse();
23515         }
23516     },
23517
23518     /**
23519      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23520      */
23521     expand : function(){
23522         if(this.isExpanded() || !this.hasFocus){
23523             return;
23524         }
23525         this.list.alignTo(this.el, this.listAlign);
23526         this.list.show();
23527         Roo.get(document).on('mousedown', this.collapseIf, this);
23528         Roo.get(document).on('mousewheel', this.collapseIf, this);
23529         if (!this.editable) {
23530             Roo.get(document).on('keydown', this.listKeyPress, this);
23531         }
23532         
23533         this.fireEvent('expand', this);
23534     },
23535
23536     // private
23537     // Implements the default empty TriggerField.onTriggerClick function
23538     onTriggerClick : function(){
23539         if(this.disabled){
23540             return;
23541         }
23542         if(this.isExpanded()){
23543             this.collapse();
23544             if (!this.blockFocus) {
23545                 this.el.focus();
23546             }
23547             
23548         }else {
23549             this.hasFocus = true;
23550             if(this.triggerAction == 'all') {
23551                 this.doQuery(this.allQuery, true);
23552             } else {
23553                 this.doQuery(this.getRawValue());
23554             }
23555             if (!this.blockFocus) {
23556                 this.el.focus();
23557             }
23558         }
23559     },
23560     listKeyPress : function(e)
23561     {
23562         //Roo.log('listkeypress');
23563         // scroll to first matching element based on key pres..
23564         if (e.isSpecialKey()) {
23565             return false;
23566         }
23567         var k = String.fromCharCode(e.getKey()).toUpperCase();
23568         //Roo.log(k);
23569         var match  = false;
23570         var csel = this.view.getSelectedNodes();
23571         var cselitem = false;
23572         if (csel.length) {
23573             var ix = this.view.indexOf(csel[0]);
23574             cselitem  = this.store.getAt(ix);
23575             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
23576                 cselitem = false;
23577             }
23578             
23579         }
23580         
23581         this.store.each(function(v) { 
23582             if (cselitem) {
23583                 // start at existing selection.
23584                 if (cselitem.id == v.id) {
23585                     cselitem = false;
23586                 }
23587                 return;
23588             }
23589                 
23590             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
23591                 match = this.store.indexOf(v);
23592                 return false;
23593             }
23594         }, this);
23595         
23596         if (match === false) {
23597             return true; // no more action?
23598         }
23599         // scroll to?
23600         this.view.select(match);
23601         var sn = Roo.get(this.view.getSelectedNodes()[0])
23602         sn.scrollIntoView(sn.dom.parentNode, false);
23603     }
23604
23605     /** 
23606     * @cfg {Boolean} grow 
23607     * @hide 
23608     */
23609     /** 
23610     * @cfg {Number} growMin 
23611     * @hide 
23612     */
23613     /** 
23614     * @cfg {Number} growMax 
23615     * @hide 
23616     */
23617     /**
23618      * @hide
23619      * @method autoSize
23620      */
23621 });/*
23622  * Based on:
23623  * Ext JS Library 1.1.1
23624  * Copyright(c) 2006-2007, Ext JS, LLC.
23625  *
23626  * Originally Released Under LGPL - original licence link has changed is not relivant.
23627  *
23628  * Fork - LGPL
23629  * <script type="text/javascript">
23630  */
23631 /**
23632  * @class Roo.form.Checkbox
23633  * @extends Roo.form.Field
23634  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
23635  * @constructor
23636  * Creates a new Checkbox
23637  * @param {Object} config Configuration options
23638  */
23639 Roo.form.Checkbox = function(config){
23640     Roo.form.Checkbox.superclass.constructor.call(this, config);
23641     this.addEvents({
23642         /**
23643          * @event check
23644          * Fires when the checkbox is checked or unchecked.
23645              * @param {Roo.form.Checkbox} this This checkbox
23646              * @param {Boolean} checked The new checked value
23647              */
23648         check : true
23649     });
23650 };
23651
23652 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
23653     /**
23654      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
23655      */
23656     focusClass : undefined,
23657     /**
23658      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
23659      */
23660     fieldClass: "x-form-field",
23661     /**
23662      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
23663      */
23664     checked: false,
23665     /**
23666      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
23667      * {tag: "input", type: "checkbox", autocomplete: "off"})
23668      */
23669     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
23670     /**
23671      * @cfg {String} boxLabel The text that appears beside the checkbox
23672      */
23673     boxLabel : "",
23674     /**
23675      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
23676      */  
23677     inputValue : '1',
23678     /**
23679      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23680      */
23681      valueOff: '0', // value when not checked..
23682
23683     actionMode : 'viewEl', 
23684     //
23685     // private
23686     itemCls : 'x-menu-check-item x-form-item',
23687     groupClass : 'x-menu-group-item',
23688     inputType : 'hidden',
23689     
23690     
23691     inSetChecked: false, // check that we are not calling self...
23692     
23693     inputElement: false, // real input element?
23694     basedOn: false, // ????
23695     
23696     isFormField: true, // not sure where this is needed!!!!
23697
23698     onResize : function(){
23699         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
23700         if(!this.boxLabel){
23701             this.el.alignTo(this.wrap, 'c-c');
23702         }
23703     },
23704
23705     initEvents : function(){
23706         Roo.form.Checkbox.superclass.initEvents.call(this);
23707         this.el.on("click", this.onClick,  this);
23708         this.el.on("change", this.onClick,  this);
23709     },
23710
23711
23712     getResizeEl : function(){
23713         return this.wrap;
23714     },
23715
23716     getPositionEl : function(){
23717         return this.wrap;
23718     },
23719
23720     // private
23721     onRender : function(ct, position){
23722         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
23723         /*
23724         if(this.inputValue !== undefined){
23725             this.el.dom.value = this.inputValue;
23726         }
23727         */
23728         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
23729         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
23730         var viewEl = this.wrap.createChild({ 
23731             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
23732         this.viewEl = viewEl;   
23733         this.wrap.on('click', this.onClick,  this); 
23734         
23735         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
23736         this.el.on('propertychange', this.setFromHidden,  this);  //ie
23737         
23738         
23739         
23740         if(this.boxLabel){
23741             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
23742         //    viewEl.on('click', this.onClick,  this); 
23743         }
23744         //if(this.checked){
23745             this.setChecked(this.checked);
23746         //}else{
23747             //this.checked = this.el.dom;
23748         //}
23749
23750     },
23751
23752     // private
23753     initValue : Roo.emptyFn,
23754
23755     /**
23756      * Returns the checked state of the checkbox.
23757      * @return {Boolean} True if checked, else false
23758      */
23759     getValue : function(){
23760         if(this.el){
23761             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
23762         }
23763         return this.valueOff;
23764         
23765     },
23766
23767         // private
23768     onClick : function(){ 
23769         this.setChecked(!this.checked);
23770
23771         //if(this.el.dom.checked != this.checked){
23772         //    this.setValue(this.el.dom.checked);
23773        // }
23774     },
23775
23776     /**
23777      * Sets the checked state of the checkbox.
23778      * On is always based on a string comparison between inputValue and the param.
23779      * @param {Boolean/String} value - the value to set 
23780      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
23781      */
23782     setValue : function(v,suppressEvent){
23783         
23784         
23785         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
23786         //if(this.el && this.el.dom){
23787         //    this.el.dom.checked = this.checked;
23788         //    this.el.dom.defaultChecked = this.checked;
23789         //}
23790         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
23791         //this.fireEvent("check", this, this.checked);
23792     },
23793     // private..
23794     setChecked : function(state,suppressEvent)
23795     {
23796         if (this.inSetChecked) {
23797             this.checked = state;
23798             return;
23799         }
23800         
23801     
23802         if(this.wrap){
23803             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
23804         }
23805         this.checked = state;
23806         if(suppressEvent !== true){
23807             this.fireEvent('check', this, state);
23808         }
23809         this.inSetChecked = true;
23810         this.el.dom.value = state ? this.inputValue : this.valueOff;
23811         this.inSetChecked = false;
23812         
23813     },
23814     // handle setting of hidden value by some other method!!?!?
23815     setFromHidden: function()
23816     {
23817         if(!this.el){
23818             return;
23819         }
23820         //console.log("SET FROM HIDDEN");
23821         //alert('setFrom hidden');
23822         this.setValue(this.el.dom.value);
23823     },
23824     
23825     onDestroy : function()
23826     {
23827         if(this.viewEl){
23828             Roo.get(this.viewEl).remove();
23829         }
23830          
23831         Roo.form.Checkbox.superclass.onDestroy.call(this);
23832     }
23833
23834 });/*
23835  * Based on:
23836  * Ext JS Library 1.1.1
23837  * Copyright(c) 2006-2007, Ext JS, LLC.
23838  *
23839  * Originally Released Under LGPL - original licence link has changed is not relivant.
23840  *
23841  * Fork - LGPL
23842  * <script type="text/javascript">
23843  */
23844  
23845 /**
23846  * @class Roo.form.Radio
23847  * @extends Roo.form.Checkbox
23848  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
23849  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
23850  * @constructor
23851  * Creates a new Radio
23852  * @param {Object} config Configuration options
23853  */
23854 Roo.form.Radio = function(){
23855     Roo.form.Radio.superclass.constructor.apply(this, arguments);
23856 };
23857 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
23858     inputType: 'radio',
23859
23860     /**
23861      * If this radio is part of a group, it will return the selected value
23862      * @return {String}
23863      */
23864     getGroupValue : function(){
23865         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
23866     }
23867 });//<script type="text/javascript">
23868
23869 /*
23870  * Ext JS Library 1.1.1
23871  * Copyright(c) 2006-2007, Ext JS, LLC.
23872  * licensing@extjs.com
23873  * 
23874  * http://www.extjs.com/license
23875  */
23876  
23877  /*
23878   * 
23879   * Known bugs:
23880   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
23881   * - IE ? - no idea how much works there.
23882   * 
23883   * 
23884   * 
23885   */
23886  
23887
23888 /**
23889  * @class Ext.form.HtmlEditor
23890  * @extends Ext.form.Field
23891  * Provides a lightweight HTML Editor component.
23892  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
23893  * 
23894  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
23895  * supported by this editor.</b><br/><br/>
23896  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
23897  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23898  */
23899 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
23900       /**
23901      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23902      */
23903     toolbars : false,
23904     /**
23905      * @cfg {String} createLinkText The default text for the create link prompt
23906      */
23907     createLinkText : 'Please enter the URL for the link:',
23908     /**
23909      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
23910      */
23911     defaultLinkValue : 'http:/'+'/',
23912    
23913     
23914     // id of frame..
23915     frameId: false,
23916     
23917     // private properties
23918     validationEvent : false,
23919     deferHeight: true,
23920     initialized : false,
23921     activated : false,
23922     sourceEditMode : false,
23923     onFocus : Roo.emptyFn,
23924     iframePad:3,
23925     hideMode:'offsets',
23926     defaultAutoCreate : {
23927         tag: "textarea",
23928         style:"width:500px;height:300px;",
23929         autocomplete: "off"
23930     },
23931
23932     // private
23933     initComponent : function(){
23934         this.addEvents({
23935             /**
23936              * @event initialize
23937              * Fires when the editor is fully initialized (including the iframe)
23938              * @param {HtmlEditor} this
23939              */
23940             initialize: true,
23941             /**
23942              * @event activate
23943              * Fires when the editor is first receives the focus. Any insertion must wait
23944              * until after this event.
23945              * @param {HtmlEditor} this
23946              */
23947             activate: true,
23948              /**
23949              * @event beforesync
23950              * Fires before the textarea is updated with content from the editor iframe. Return false
23951              * to cancel the sync.
23952              * @param {HtmlEditor} this
23953              * @param {String} html
23954              */
23955             beforesync: true,
23956              /**
23957              * @event beforepush
23958              * Fires before the iframe editor is updated with content from the textarea. Return false
23959              * to cancel the push.
23960              * @param {HtmlEditor} this
23961              * @param {String} html
23962              */
23963             beforepush: true,
23964              /**
23965              * @event sync
23966              * Fires when the textarea is updated with content from the editor iframe.
23967              * @param {HtmlEditor} this
23968              * @param {String} html
23969              */
23970             sync: true,
23971              /**
23972              * @event push
23973              * Fires when the iframe editor is updated with content from the textarea.
23974              * @param {HtmlEditor} this
23975              * @param {String} html
23976              */
23977             push: true,
23978              /**
23979              * @event editmodechange
23980              * Fires when the editor switches edit modes
23981              * @param {HtmlEditor} this
23982              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23983              */
23984             editmodechange: true,
23985             /**
23986              * @event editorevent
23987              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23988              * @param {HtmlEditor} this
23989              */
23990             editorevent: true
23991         })
23992     },
23993
23994     /**
23995      * Protected method that will not generally be called directly. It
23996      * is called when the editor creates its toolbar. Override this method if you need to
23997      * add custom toolbar buttons.
23998      * @param {HtmlEditor} editor
23999      */
24000     createToolbar : function(editor){
24001         if (!editor.toolbars || !editor.toolbars.length) {
24002             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
24003         }
24004         
24005         for (var i =0 ; i < editor.toolbars.length;i++) {
24006             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
24007             editor.toolbars[i].init(editor);
24008         }
24009          
24010         
24011     },
24012
24013     /**
24014      * Protected method that will not generally be called directly. It
24015      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24016      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24017      */
24018     getDocMarkup : function(){
24019         return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
24020     },
24021
24022     // private
24023     onRender : function(ct, position){
24024         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
24025         this.el.dom.style.border = '0 none';
24026         this.el.dom.setAttribute('tabIndex', -1);
24027         this.el.addClass('x-hidden');
24028         if(Roo.isIE){ // fix IE 1px bogus margin
24029             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24030         }
24031         this.wrap = this.el.wrap({
24032             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
24033         });
24034
24035         this.frameId = Roo.id();
24036         this.createToolbar(this);
24037         
24038         
24039         
24040         
24041       
24042         
24043         var iframe = this.wrap.createChild({
24044             tag: 'iframe',
24045             id: this.frameId,
24046             name: this.frameId,
24047             frameBorder : 'no',
24048             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24049         });
24050         
24051        // console.log(iframe);
24052         //this.wrap.dom.appendChild(iframe);
24053
24054         this.iframe = iframe.dom;
24055
24056          this.assignDocWin();
24057         
24058         this.doc.designMode = 'on';
24059        
24060         this.doc.open();
24061         this.doc.write(this.getDocMarkup());
24062         this.doc.close();
24063
24064         
24065         var task = { // must defer to wait for browser to be ready
24066             run : function(){
24067                 //console.log("run task?" + this.doc.readyState);
24068                 this.assignDocWin();
24069                 if(this.doc.body || this.doc.readyState == 'complete'){
24070                     try {
24071                         this.doc.designMode="on";
24072                     } catch (e) {
24073                         return;
24074                     }
24075                     Roo.TaskMgr.stop(task);
24076                     this.initEditor.defer(10, this);
24077                 }
24078             },
24079             interval : 10,
24080             duration:10000,
24081             scope: this
24082         };
24083         Roo.TaskMgr.start(task);
24084
24085         if(!this.width){
24086             this.setSize(this.el.getSize());
24087         }
24088     },
24089
24090     // private
24091     onResize : function(w, h){
24092         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
24093         if(this.el && this.iframe){
24094             if(typeof w == 'number'){
24095                 var aw = w - this.wrap.getFrameWidth('lr');
24096                 this.el.setWidth(this.adjustWidth('textarea', aw));
24097                 this.iframe.style.width = aw + 'px';
24098             }
24099             if(typeof h == 'number'){
24100                 var tbh = 0;
24101                 for (var i =0; i < this.toolbars.length;i++) {
24102                     // fixme - ask toolbars for heights?
24103                     tbh += this.toolbars[i].tb.el.getHeight();
24104                 }
24105                 
24106                 
24107                 
24108                 
24109                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24110                 this.el.setHeight(this.adjustWidth('textarea', ah));
24111                 this.iframe.style.height = ah + 'px';
24112                 if(this.doc){
24113                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
24114                 }
24115             }
24116         }
24117     },
24118
24119     /**
24120      * Toggles the editor between standard and source edit mode.
24121      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24122      */
24123     toggleSourceEdit : function(sourceEditMode){
24124         
24125         this.sourceEditMode = sourceEditMode === true;
24126         
24127         if(this.sourceEditMode){
24128           
24129             this.syncValue();
24130             this.iframe.className = 'x-hidden';
24131             this.el.removeClass('x-hidden');
24132             this.el.dom.removeAttribute('tabIndex');
24133             this.el.focus();
24134         }else{
24135              
24136             this.pushValue();
24137             this.iframe.className = '';
24138             this.el.addClass('x-hidden');
24139             this.el.dom.setAttribute('tabIndex', -1);
24140             this.deferFocus();
24141         }
24142         this.setSize(this.wrap.getSize());
24143         this.fireEvent('editmodechange', this, this.sourceEditMode);
24144     },
24145
24146     // private used internally
24147     createLink : function(){
24148         var url = prompt(this.createLinkText, this.defaultLinkValue);
24149         if(url && url != 'http:/'+'/'){
24150             this.relayCmd('createlink', url);
24151         }
24152     },
24153
24154     // private (for BoxComponent)
24155     adjustSize : Roo.BoxComponent.prototype.adjustSize,
24156
24157     // private (for BoxComponent)
24158     getResizeEl : function(){
24159         return this.wrap;
24160     },
24161
24162     // private (for BoxComponent)
24163     getPositionEl : function(){
24164         return this.wrap;
24165     },
24166
24167     // private
24168     initEvents : function(){
24169         this.originalValue = this.getValue();
24170     },
24171
24172     /**
24173      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24174      * @method
24175      */
24176     markInvalid : Roo.emptyFn,
24177     /**
24178      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24179      * @method
24180      */
24181     clearInvalid : Roo.emptyFn,
24182
24183     setValue : function(v){
24184         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
24185         this.pushValue();
24186     },
24187
24188     /**
24189      * Protected method that will not generally be called directly. If you need/want
24190      * custom HTML cleanup, this is the method you should override.
24191      * @param {String} html The HTML to be cleaned
24192      * return {String} The cleaned HTML
24193      */
24194     cleanHtml : function(html){
24195         html = String(html);
24196         if(html.length > 5){
24197             if(Roo.isSafari){ // strip safari nonsense
24198                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24199             }
24200         }
24201         if(html == '&nbsp;'){
24202             html = '';
24203         }
24204         return html;
24205     },
24206
24207     /**
24208      * Protected method that will not generally be called directly. Syncs the contents
24209      * of the editor iframe with the textarea.
24210      */
24211     syncValue : function(){
24212         if(this.initialized){
24213             var bd = (this.doc.body || this.doc.documentElement);
24214             var html = bd.innerHTML;
24215             if(Roo.isSafari){
24216                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24217                 var m = bs.match(/text-align:(.*?);/i);
24218                 if(m && m[1]){
24219                     html = '<div style="'+m[0]+'">' + html + '</div>';
24220                 }
24221             }
24222             html = this.cleanHtml(html);
24223             if(this.fireEvent('beforesync', this, html) !== false){
24224                 this.el.dom.value = html;
24225                 this.fireEvent('sync', this, html);
24226             }
24227         }
24228     },
24229
24230     /**
24231      * Protected method that will not generally be called directly. Pushes the value of the textarea
24232      * into the iframe editor.
24233      */
24234     pushValue : function(){
24235         if(this.initialized){
24236             var v = this.el.dom.value;
24237             if(v.length < 1){
24238                 v = '&#160;';
24239             }
24240             if(this.fireEvent('beforepush', this, v) !== false){
24241                 (this.doc.body || this.doc.documentElement).innerHTML = v;
24242                 this.fireEvent('push', this, v);
24243             }
24244         }
24245     },
24246
24247     // private
24248     deferFocus : function(){
24249         this.focus.defer(10, this);
24250     },
24251
24252     // doc'ed in Field
24253     focus : function(){
24254         if(this.win && !this.sourceEditMode){
24255             this.win.focus();
24256         }else{
24257             this.el.focus();
24258         }
24259     },
24260     
24261     assignDocWin: function()
24262     {
24263         var iframe = this.iframe;
24264         
24265          if(Roo.isIE){
24266             this.doc = iframe.contentWindow.document;
24267             this.win = iframe.contentWindow;
24268         } else {
24269             if (!Roo.get(this.frameId)) {
24270                 return;
24271             }
24272             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24273             this.win = Roo.get(this.frameId).dom.contentWindow;
24274         }
24275     },
24276     
24277     // private
24278     initEditor : function(){
24279         //console.log("INIT EDITOR");
24280         this.assignDocWin();
24281         
24282         
24283         
24284         this.doc.designMode="on";
24285         this.doc.open();
24286         this.doc.write(this.getDocMarkup());
24287         this.doc.close();
24288         
24289         var dbody = (this.doc.body || this.doc.documentElement);
24290         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24291         // this copies styles from the containing element into thsi one..
24292         // not sure why we need all of this..
24293         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24294         ss['background-attachment'] = 'fixed'; // w3c
24295         dbody.bgProperties = 'fixed'; // ie
24296         Roo.DomHelper.applyStyles(dbody, ss);
24297         Roo.EventManager.on(this.doc, {
24298             'mousedown': this.onEditorEvent,
24299             'dblclick': this.onEditorEvent,
24300             'click': this.onEditorEvent,
24301             'keyup': this.onEditorEvent,
24302             buffer:100,
24303             scope: this
24304         });
24305         if(Roo.isGecko){
24306             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24307         }
24308         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24309             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24310         }
24311         this.initialized = true;
24312
24313         this.fireEvent('initialize', this);
24314         this.pushValue();
24315     },
24316
24317     // private
24318     onDestroy : function(){
24319         
24320         
24321         
24322         if(this.rendered){
24323             
24324             for (var i =0; i < this.toolbars.length;i++) {
24325                 // fixme - ask toolbars for heights?
24326                 this.toolbars[i].onDestroy();
24327             }
24328             
24329             this.wrap.dom.innerHTML = '';
24330             this.wrap.remove();
24331         }
24332     },
24333
24334     // private
24335     onFirstFocus : function(){
24336         
24337         this.assignDocWin();
24338         
24339         
24340         this.activated = true;
24341         for (var i =0; i < this.toolbars.length;i++) {
24342             this.toolbars[i].onFirstFocus();
24343         }
24344        
24345         if(Roo.isGecko){ // prevent silly gecko errors
24346             this.win.focus();
24347             var s = this.win.getSelection();
24348             if(!s.focusNode || s.focusNode.nodeType != 3){
24349                 var r = s.getRangeAt(0);
24350                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24351                 r.collapse(true);
24352                 this.deferFocus();
24353             }
24354             try{
24355                 this.execCmd('useCSS', true);
24356                 this.execCmd('styleWithCSS', false);
24357             }catch(e){}
24358         }
24359         this.fireEvent('activate', this);
24360     },
24361
24362     // private
24363     adjustFont: function(btn){
24364         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24365         //if(Roo.isSafari){ // safari
24366         //    adjust *= 2;
24367        // }
24368         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24369         if(Roo.isSafari){ // safari
24370             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24371             v =  (v < 10) ? 10 : v;
24372             v =  (v > 48) ? 48 : v;
24373             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24374             
24375         }
24376         
24377         
24378         v = Math.max(1, v+adjust);
24379         
24380         this.execCmd('FontSize', v  );
24381     },
24382
24383     onEditorEvent : function(e){
24384         this.fireEvent('editorevent', this, e);
24385       //  this.updateToolbar();
24386         this.syncValue();
24387     },
24388
24389     insertTag : function(tg)
24390     {
24391         // could be a bit smarter... -> wrap the current selected tRoo..
24392         
24393         this.execCmd("formatblock",   tg);
24394         
24395     },
24396     
24397     insertText : function(txt)
24398     {
24399         
24400         
24401         range = this.createRange();
24402         range.deleteContents();
24403                //alert(Sender.getAttribute('label'));
24404                
24405         range.insertNode(this.doc.createTextNode(txt));
24406     } ,
24407     
24408     // private
24409     relayBtnCmd : function(btn){
24410         this.relayCmd(btn.cmd);
24411     },
24412
24413     /**
24414      * Executes a Midas editor command on the editor document and performs necessary focus and
24415      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24416      * @param {String} cmd The Midas command
24417      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24418      */
24419     relayCmd : function(cmd, value){
24420         this.win.focus();
24421         this.execCmd(cmd, value);
24422         this.fireEvent('editorevent', this);
24423         //this.updateToolbar();
24424         this.deferFocus();
24425     },
24426
24427     /**
24428      * Executes a Midas editor command directly on the editor document.
24429      * For visual commands, you should use {@link #relayCmd} instead.
24430      * <b>This should only be called after the editor is initialized.</b>
24431      * @param {String} cmd The Midas command
24432      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24433      */
24434     execCmd : function(cmd, value){
24435         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24436         this.syncValue();
24437     },
24438
24439    
24440     /**
24441      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24442      * to insert tRoo.
24443      * @param {String} text
24444      */
24445     insertAtCursor : function(text){
24446         if(!this.activated){
24447             return;
24448         }
24449         if(Roo.isIE){
24450             this.win.focus();
24451             var r = this.doc.selection.createRange();
24452             if(r){
24453                 r.collapse(true);
24454                 r.pasteHTML(text);
24455                 this.syncValue();
24456                 this.deferFocus();
24457             }
24458         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24459             this.win.focus();
24460             this.execCmd('InsertHTML', text);
24461             this.deferFocus();
24462         }
24463     },
24464  // private
24465     mozKeyPress : function(e){
24466         if(e.ctrlKey){
24467             var c = e.getCharCode(), cmd;
24468           
24469             if(c > 0){
24470                 c = String.fromCharCode(c).toLowerCase();
24471                 switch(c){
24472                     case 'b':
24473                         cmd = 'bold';
24474                     break;
24475                     case 'i':
24476                         cmd = 'italic';
24477                     break;
24478                     case 'u':
24479                         cmd = 'underline';
24480                     case 'v':
24481                         this.cleanUpPaste.defer(100, this);
24482                         return;
24483                     break;
24484                 }
24485                 if(cmd){
24486                     this.win.focus();
24487                     this.execCmd(cmd);
24488                     this.deferFocus();
24489                     e.preventDefault();
24490                 }
24491                 
24492             }
24493         }
24494     },
24495
24496     // private
24497     fixKeys : function(){ // load time branching for fastest keydown performance
24498         if(Roo.isIE){
24499             return function(e){
24500                 var k = e.getKey(), r;
24501                 if(k == e.TAB){
24502                     e.stopEvent();
24503                     r = this.doc.selection.createRange();
24504                     if(r){
24505                         r.collapse(true);
24506                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24507                         this.deferFocus();
24508                     }
24509                     return;
24510                 }
24511                 
24512                 if(k == e.ENTER){
24513                     r = this.doc.selection.createRange();
24514                     if(r){
24515                         var target = r.parentElement();
24516                         if(!target || target.tagName.toLowerCase() != 'li'){
24517                             e.stopEvent();
24518                             r.pasteHTML('<br />');
24519                             r.collapse(false);
24520                             r.select();
24521                         }
24522                     }
24523                 }
24524                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24525                     this.cleanUpPaste.defer(100, this);
24526                     return;
24527                 }
24528                 
24529                 
24530             };
24531         }else if(Roo.isOpera){
24532             return function(e){
24533                 var k = e.getKey();
24534                 if(k == e.TAB){
24535                     e.stopEvent();
24536                     this.win.focus();
24537                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24538                     this.deferFocus();
24539                 }
24540                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24541                     this.cleanUpPaste.defer(100, this);
24542                     return;
24543                 }
24544                 
24545             };
24546         }else if(Roo.isSafari){
24547             return function(e){
24548                 var k = e.getKey();
24549                 
24550                 if(k == e.TAB){
24551                     e.stopEvent();
24552                     this.execCmd('InsertText','\t');
24553                     this.deferFocus();
24554                     return;
24555                 }
24556                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24557                     this.cleanUpPaste.defer(100, this);
24558                     return;
24559                 }
24560                 
24561              };
24562         }
24563     }(),
24564     
24565     getAllAncestors: function()
24566     {
24567         var p = this.getSelectedNode();
24568         var a = [];
24569         if (!p) {
24570             a.push(p); // push blank onto stack..
24571             p = this.getParentElement();
24572         }
24573         
24574         
24575         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24576             a.push(p);
24577             p = p.parentNode;
24578         }
24579         a.push(this.doc.body);
24580         return a;
24581     },
24582     lastSel : false,
24583     lastSelNode : false,
24584     
24585     
24586     getSelection : function() 
24587     {
24588         this.assignDocWin();
24589         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24590     },
24591     
24592     getSelectedNode: function() 
24593     {
24594         // this may only work on Gecko!!!
24595         
24596         // should we cache this!!!!
24597         
24598         
24599         
24600          
24601         var range = this.createRange(this.getSelection());
24602         
24603         if (Roo.isIE) {
24604             var parent = range.parentElement();
24605             while (true) {
24606                 var testRange = range.duplicate();
24607                 testRange.moveToElementText(parent);
24608                 if (testRange.inRange(range)) {
24609                     break;
24610                 }
24611                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24612                     break;
24613                 }
24614                 parent = parent.parentElement;
24615             }
24616             return parent;
24617         }
24618         
24619         
24620         var ar = range.endContainer.childNodes;
24621         if (!ar.length) {
24622             ar = range.commonAncestorContainer.childNodes;
24623             //alert(ar.length);
24624         }
24625         var nodes = [];
24626         var other_nodes = [];
24627         var has_other_nodes = false;
24628         for (var i=0;i<ar.length;i++) {
24629             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24630                 continue;
24631             }
24632             // fullly contained node.
24633             
24634             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24635                 nodes.push(ar[i]);
24636                 continue;
24637             }
24638             
24639             // probably selected..
24640             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24641                 other_nodes.push(ar[i]);
24642                 continue;
24643             }
24644             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24645                 continue;
24646             }
24647             
24648             
24649             has_other_nodes = true;
24650         }
24651         if (!nodes.length && other_nodes.length) {
24652             nodes= other_nodes;
24653         }
24654         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24655             return false;
24656         }
24657         
24658         return nodes[0];
24659     },
24660     createRange: function(sel)
24661     {
24662         // this has strange effects when using with 
24663         // top toolbar - not sure if it's a great idea.
24664         //this.editor.contentWindow.focus();
24665         if (typeof sel != "undefined") {
24666             try {
24667                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24668             } catch(e) {
24669                 return this.doc.createRange();
24670             }
24671         } else {
24672             return this.doc.createRange();
24673         }
24674     },
24675     getParentElement: function()
24676     {
24677         
24678         this.assignDocWin();
24679         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24680         
24681         var range = this.createRange(sel);
24682          
24683         try {
24684             var p = range.commonAncestorContainer;
24685             while (p.nodeType == 3) { // text node
24686                 p = p.parentNode;
24687             }
24688             return p;
24689         } catch (e) {
24690             return null;
24691         }
24692     
24693     },
24694     
24695     
24696     
24697     // BC Hacks - cause I cant work out what i was trying to do..
24698     rangeIntersectsNode : function(range, node)
24699     {
24700         var nodeRange = node.ownerDocument.createRange();
24701         try {
24702             nodeRange.selectNode(node);
24703         }
24704         catch (e) {
24705             nodeRange.selectNodeContents(node);
24706         }
24707
24708         return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
24709                  range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
24710     },
24711     rangeCompareNode : function(range, node) {
24712         var nodeRange = node.ownerDocument.createRange();
24713         try {
24714             nodeRange.selectNode(node);
24715         } catch (e) {
24716             nodeRange.selectNodeContents(node);
24717         }
24718         var nodeIsBefore = range.compareBoundaryPoints(Range.START_TO_START, nodeRange) == 1;
24719         var nodeIsAfter = range.compareBoundaryPoints(Range.END_TO_END, nodeRange) == -1;
24720
24721         if (nodeIsBefore && !nodeIsAfter)
24722             return 0;
24723         if (!nodeIsBefore && nodeIsAfter)
24724             return 1;
24725         if (nodeIsBefore && nodeIsAfter)
24726             return 2;
24727
24728         return 3;
24729     },
24730
24731     // private? - in a new class?
24732     cleanUpPaste :  function()
24733     {
24734         // cleans up the whole document..
24735       //  console.log('cleanuppaste');
24736         this.cleanUpChildren(this.doc.body)
24737         
24738         
24739     },
24740     cleanUpChildren : function (n)
24741     {
24742         if (!n.childNodes.length) {
24743             return;
24744         }
24745         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24746            this.cleanUpChild(n.childNodes[i]);
24747         }
24748     },
24749     
24750     
24751         
24752     
24753     cleanUpChild : function (node)
24754     {
24755         //console.log(node);
24756         if (node.nodeName == "#text") {
24757             // clean up silly Windows -- stuff?
24758             return; 
24759         }
24760         if (node.nodeName == "#comment") {
24761             node.parentNode.removeChild(node);
24762             // clean up silly Windows -- stuff?
24763             return; 
24764         }
24765         
24766         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
24767             // remove node.
24768             node.parentNode.removeChild(node);
24769             return;
24770             
24771         }
24772         if (!node.attributes || !node.attributes.length) {
24773             this.cleanUpChildren(node);
24774             return;
24775         }
24776         
24777         function cleanAttr(n,v)
24778         {
24779             
24780             if (v.match(/^\./) || v.match(/^\//)) {
24781                 return;
24782             }
24783             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
24784                 return;
24785             }
24786             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
24787             node.removeAttribute(n);
24788             
24789         }
24790         
24791         function cleanStyle(n,v)
24792         {
24793             if (v.match(/expression/)) { //XSS?? should we even bother..
24794                 node.removeAttribute(n);
24795                 return;
24796             }
24797             
24798             
24799             var parts = v.split(/;/);
24800             Roo.each(parts, function(p) {
24801                 p = p.replace(/\s+/g,'');
24802                 if (!p.length) {
24803                     return;
24804                 }
24805                 var l = p.split(':').shift().replace(/\s+/g,'');
24806                 
24807                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
24808                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
24809                     node.removeAttribute(n);
24810                     return false;
24811                 }
24812             });
24813             
24814             
24815         }
24816         
24817         
24818         for (var i = node.attributes.length-1; i > -1 ; i--) {
24819             var a = node.attributes[i];
24820             //console.log(a);
24821             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
24822                 node.removeAttribute(a.name);
24823                 return;
24824             }
24825             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
24826                 cleanAttr(a.name,a.value); // fixme..
24827                 return;
24828             }
24829             if (a.name == 'style') {
24830                 cleanStyle(a.name,a.value);
24831             }
24832             /// clean up MS crap..
24833             if (a.name == 'class') {
24834                 if (a.value.match(/^Mso/)) {
24835                     node.className = '';
24836                 }
24837             }
24838             
24839             // style cleanup!?
24840             // class cleanup?
24841             
24842         }
24843         
24844         
24845         this.cleanUpChildren(node);
24846         
24847         
24848     }
24849     
24850     
24851     // hide stuff that is not compatible
24852     /**
24853      * @event blur
24854      * @hide
24855      */
24856     /**
24857      * @event change
24858      * @hide
24859      */
24860     /**
24861      * @event focus
24862      * @hide
24863      */
24864     /**
24865      * @event specialkey
24866      * @hide
24867      */
24868     /**
24869      * @cfg {String} fieldClass @hide
24870      */
24871     /**
24872      * @cfg {String} focusClass @hide
24873      */
24874     /**
24875      * @cfg {String} autoCreate @hide
24876      */
24877     /**
24878      * @cfg {String} inputType @hide
24879      */
24880     /**
24881      * @cfg {String} invalidClass @hide
24882      */
24883     /**
24884      * @cfg {String} invalidText @hide
24885      */
24886     /**
24887      * @cfg {String} msgFx @hide
24888      */
24889     /**
24890      * @cfg {String} validateOnBlur @hide
24891      */
24892 });
24893
24894 Roo.form.HtmlEditor.white = [
24895         'area', 'br', 'img', 'input', 'hr', 'wbr',
24896         
24897        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
24898        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
24899        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
24900        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
24901        'table',   'ul',         'xmp', 
24902        
24903        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
24904       'thead',   'tr', 
24905      
24906       'dir', 'menu', 'ol', 'ul', 'dl',
24907        
24908       'embed',  'object'
24909 ];
24910
24911
24912 Roo.form.HtmlEditor.black = [
24913     //    'embed',  'object', // enable - backend responsiblity to clean thiese
24914         'applet', // 
24915         'base',   'basefont', 'bgsound', 'blink',  'body', 
24916         'frame',  'frameset', 'head',    'html',   'ilayer', 
24917         'iframe', 'layer',  'link',     'meta',    'object',   
24918         'script', 'style' ,'title',  'xml' // clean later..
24919 ];
24920 Roo.form.HtmlEditor.clean = [
24921     'script', 'style', 'title', 'xml'
24922 ];
24923
24924 // attributes..
24925
24926 Roo.form.HtmlEditor.ablack = [
24927     'on'
24928 ];
24929     
24930 Roo.form.HtmlEditor.aclean = [ 
24931     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
24932 ];
24933
24934 // protocols..
24935 Roo.form.HtmlEditor.pwhite= [
24936         'http',  'https',  'mailto'
24937 ];
24938
24939 Roo.form.HtmlEditor.cwhite= [
24940         'text-align',
24941         'font-size'
24942 ];
24943
24944 // <script type="text/javascript">
24945 /*
24946  * Based on
24947  * Ext JS Library 1.1.1
24948  * Copyright(c) 2006-2007, Ext JS, LLC.
24949  *  
24950  
24951  */
24952
24953 /**
24954  * @class Roo.form.HtmlEditorToolbar1
24955  * Basic Toolbar
24956  * 
24957  * Usage:
24958  *
24959  new Roo.form.HtmlEditor({
24960     ....
24961     toolbars : [
24962         new Roo.form.HtmlEditorToolbar1({
24963             disable : { fonts: 1 , format: 1, ..., ... , ...],
24964             btns : [ .... ]
24965         })
24966     }
24967      
24968  * 
24969  * @cfg {Object} disable List of elements to disable..
24970  * @cfg {Array} btns List of additional buttons.
24971  * 
24972  * 
24973  * NEEDS Extra CSS? 
24974  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24975  */
24976  
24977 Roo.form.HtmlEditor.ToolbarStandard = function(config)
24978 {
24979     
24980     Roo.apply(this, config);
24981     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24982     // dont call parent... till later.
24983 }
24984
24985 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
24986     
24987     tb: false,
24988     
24989     rendered: false,
24990     
24991     editor : false,
24992     /**
24993      * @cfg {Object} disable  List of toolbar elements to disable
24994          
24995      */
24996     disable : false,
24997       /**
24998      * @cfg {Array} fontFamilies An array of available font families
24999      */
25000     fontFamilies : [
25001         'Arial',
25002         'Courier New',
25003         'Tahoma',
25004         'Times New Roman',
25005         'Verdana'
25006     ],
25007     
25008     specialChars : [
25009            "&#169;",
25010           "&#174;",     
25011           "&#8482;",    
25012           "&#163;" ,    
25013          // "&#8212;",    
25014           "&#8230;",    
25015           "&#247;" ,    
25016         //  "&#225;" ,     ?? a acute?
25017            "&#8364;"    , //Euro
25018        //   "&#8220;"    ,
25019         //  "&#8221;"    ,
25020         //  "&#8226;"    ,
25021           "&#176;"  //   , // degrees
25022
25023          // "&#233;"     , // e ecute
25024          // "&#250;"     , // u ecute?
25025     ],
25026     inputElements : [ 
25027             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
25028             "input:submit", "input:button", "select", "textarea", "label" ],
25029     formats : [
25030         ["p"] ,  
25031         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
25032         ["pre"],[ "code"], 
25033         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
25034     ],
25035      /**
25036      * @cfg {String} defaultFont default font to use.
25037      */
25038     defaultFont: 'tahoma',
25039    
25040     fontSelect : false,
25041     
25042     
25043     formatCombo : false,
25044     
25045     init : function(editor)
25046     {
25047         this.editor = editor;
25048         
25049         
25050         var fid = editor.frameId;
25051         var etb = this;
25052         function btn(id, toggle, handler){
25053             var xid = fid + '-'+ id ;
25054             return {
25055                 id : xid,
25056                 cmd : id,
25057                 cls : 'x-btn-icon x-edit-'+id,
25058                 enableToggle:toggle !== false,
25059                 scope: editor, // was editor...
25060                 handler:handler||editor.relayBtnCmd,
25061                 clickEvent:'mousedown',
25062                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
25063                 tabIndex:-1
25064             };
25065         }
25066         
25067         
25068         
25069         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
25070         this.tb = tb;
25071          // stop form submits
25072         tb.el.on('click', function(e){
25073             e.preventDefault(); // what does this do?
25074         });
25075
25076         if(!this.disable.font && !Roo.isSafari){
25077             /* why no safari for fonts
25078             editor.fontSelect = tb.el.createChild({
25079                 tag:'select',
25080                 tabIndex: -1,
25081                 cls:'x-font-select',
25082                 html: editor.createFontOptions()
25083             });
25084             editor.fontSelect.on('change', function(){
25085                 var font = editor.fontSelect.dom.value;
25086                 editor.relayCmd('fontname', font);
25087                 editor.deferFocus();
25088             }, editor);
25089             tb.add(
25090                 editor.fontSelect.dom,
25091                 '-'
25092             );
25093             */
25094         };
25095         if(!this.disable.formats){
25096             this.formatCombo = new Roo.form.ComboBox({
25097                 store: new Roo.data.SimpleStore({
25098                     id : 'tag',
25099                     fields: ['tag'],
25100                     data : this.formats // from states.js
25101                 }),
25102                 blockFocus : true,
25103                 //autoCreate : {tag: "div",  size: "20"},
25104                 displayField:'tag',
25105                 typeAhead: false,
25106                 mode: 'local',
25107                 editable : false,
25108                 triggerAction: 'all',
25109                 emptyText:'Add tag',
25110                 selectOnFocus:true,
25111                 width:135,
25112                 listeners : {
25113                     'select': function(c, r, i) {
25114                         editor.insertTag(r.get('tag'));
25115                         editor.focus();
25116                     }
25117                 }
25118
25119             });
25120             tb.addField(this.formatCombo);
25121             
25122         }
25123         
25124         if(!this.disable.format){
25125             tb.add(
25126                 btn('bold'),
25127                 btn('italic'),
25128                 btn('underline')
25129             );
25130         };
25131         if(!this.disable.fontSize){
25132             tb.add(
25133                 '-',
25134                 
25135                 
25136                 btn('increasefontsize', false, editor.adjustFont),
25137                 btn('decreasefontsize', false, editor.adjustFont)
25138             );
25139         };
25140         
25141         
25142         if(this.disable.colors){
25143             tb.add(
25144                 '-', {
25145                     id:editor.frameId +'-forecolor',
25146                     cls:'x-btn-icon x-edit-forecolor',
25147                     clickEvent:'mousedown',
25148                     tooltip: this.buttonTips['forecolor'] || undefined,
25149                     tabIndex:-1,
25150                     menu : new Roo.menu.ColorMenu({
25151                         allowReselect: true,
25152                         focus: Roo.emptyFn,
25153                         value:'000000',
25154                         plain:true,
25155                         selectHandler: function(cp, color){
25156                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
25157                             editor.deferFocus();
25158                         },
25159                         scope: editor,
25160                         clickEvent:'mousedown'
25161                     })
25162                 }, {
25163                     id:editor.frameId +'backcolor',
25164                     cls:'x-btn-icon x-edit-backcolor',
25165                     clickEvent:'mousedown',
25166                     tooltip: this.buttonTips['backcolor'] || undefined,
25167                     tabIndex:-1,
25168                     menu : new Roo.menu.ColorMenu({
25169                         focus: Roo.emptyFn,
25170                         value:'FFFFFF',
25171                         plain:true,
25172                         allowReselect: true,
25173                         selectHandler: function(cp, color){
25174                             if(Roo.isGecko){
25175                                 editor.execCmd('useCSS', false);
25176                                 editor.execCmd('hilitecolor', color);
25177                                 editor.execCmd('useCSS', true);
25178                                 editor.deferFocus();
25179                             }else{
25180                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
25181                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
25182                                 editor.deferFocus();
25183                             }
25184                         },
25185                         scope:editor,
25186                         clickEvent:'mousedown'
25187                     })
25188                 }
25189             );
25190         };
25191         // now add all the items...
25192         
25193
25194         if(!this.disable.alignments){
25195             tb.add(
25196                 '-',
25197                 btn('justifyleft'),
25198                 btn('justifycenter'),
25199                 btn('justifyright')
25200             );
25201         };
25202
25203         //if(!Roo.isSafari){
25204             if(!this.disable.links){
25205                 tb.add(
25206                     '-',
25207                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
25208                 );
25209             };
25210
25211             if(!this.disable.lists){
25212                 tb.add(
25213                     '-',
25214                     btn('insertorderedlist'),
25215                     btn('insertunorderedlist')
25216                 );
25217             }
25218             if(!this.disable.sourceEdit){
25219                 tb.add(
25220                     '-',
25221                     btn('sourceedit', true, function(btn){
25222                         this.toggleSourceEdit(btn.pressed);
25223                     })
25224                 );
25225             }
25226         //}
25227         
25228         var smenu = { };
25229         // special menu.. - needs to be tidied up..
25230         if (!this.disable.special) {
25231             smenu = {
25232                 text: "&#169;",
25233                 cls: 'x-edit-none',
25234                 menu : {
25235                     items : []
25236                    }
25237             };
25238             for (var i =0; i < this.specialChars.length; i++) {
25239                 smenu.menu.items.push({
25240                     
25241                     html: this.specialChars[i],
25242                     handler: function(a,b) {
25243                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
25244                         
25245                     },
25246                     tabIndex:-1
25247                 });
25248             }
25249             
25250             
25251             tb.add(smenu);
25252             
25253             
25254         }
25255         if (this.btns) {
25256             for(var i =0; i< this.btns.length;i++) {
25257                 var b = this.btns[i];
25258                 b.cls =  'x-edit-none';
25259                 b.scope = editor;
25260                 tb.add(b);
25261             }
25262         
25263         }
25264         
25265         
25266         
25267         // disable everything...
25268         
25269         this.tb.items.each(function(item){
25270            if(item.id != editor.frameId+ '-sourceedit'){
25271                 item.disable();
25272             }
25273         });
25274         this.rendered = true;
25275         
25276         // the all the btns;
25277         editor.on('editorevent', this.updateToolbar, this);
25278         // other toolbars need to implement this..
25279         //editor.on('editmodechange', this.updateToolbar, this);
25280     },
25281     
25282     
25283     
25284     /**
25285      * Protected method that will not generally be called directly. It triggers
25286      * a toolbar update by reading the markup state of the current selection in the editor.
25287      */
25288     updateToolbar: function(){
25289
25290         if(!this.editor.activated){
25291             this.editor.onFirstFocus();
25292             return;
25293         }
25294
25295         var btns = this.tb.items.map, 
25296             doc = this.editor.doc,
25297             frameId = this.editor.frameId;
25298
25299         if(!this.disable.font && !Roo.isSafari){
25300             /*
25301             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
25302             if(name != this.fontSelect.dom.value){
25303                 this.fontSelect.dom.value = name;
25304             }
25305             */
25306         }
25307         if(!this.disable.format){
25308             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
25309             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
25310             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
25311         }
25312         if(!this.disable.alignments){
25313             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
25314             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
25315             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
25316         }
25317         if(!Roo.isSafari && !this.disable.lists){
25318             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
25319             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
25320         }
25321         
25322         var ans = this.editor.getAllAncestors();
25323         if (this.formatCombo) {
25324             
25325             
25326             var store = this.formatCombo.store;
25327             this.formatCombo.setValue("");
25328             for (var i =0; i < ans.length;i++) {
25329                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
25330                     // select it..
25331                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
25332                     break;
25333                 }
25334             }
25335         }
25336         
25337         
25338         
25339         // hides menus... - so this cant be on a menu...
25340         Roo.menu.MenuMgr.hideAll();
25341
25342         //this.editorsyncValue();
25343     },
25344    
25345     
25346     createFontOptions : function(){
25347         var buf = [], fs = this.fontFamilies, ff, lc;
25348         for(var i = 0, len = fs.length; i< len; i++){
25349             ff = fs[i];
25350             lc = ff.toLowerCase();
25351             buf.push(
25352                 '<option value="',lc,'" style="font-family:',ff,';"',
25353                     (this.defaultFont == lc ? ' selected="true">' : '>'),
25354                     ff,
25355                 '</option>'
25356             );
25357         }
25358         return buf.join('');
25359     },
25360     
25361     toggleSourceEdit : function(sourceEditMode){
25362         if(sourceEditMode === undefined){
25363             sourceEditMode = !this.sourceEditMode;
25364         }
25365         this.sourceEditMode = sourceEditMode === true;
25366         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
25367         // just toggle the button?
25368         if(btn.pressed !== this.editor.sourceEditMode){
25369             btn.toggle(this.editor.sourceEditMode);
25370             return;
25371         }
25372         
25373         if(this.sourceEditMode){
25374             this.tb.items.each(function(item){
25375                 if(item.cmd != 'sourceedit'){
25376                     item.disable();
25377                 }
25378             });
25379           
25380         }else{
25381             if(this.initialized){
25382                 this.tb.items.each(function(item){
25383                     item.enable();
25384                 });
25385             }
25386             
25387         }
25388         // tell the editor that it's been pressed..
25389         this.editor.toggleSourceEdit(sourceEditMode);
25390        
25391     },
25392      /**
25393      * Object collection of toolbar tooltips for the buttons in the editor. The key
25394      * is the command id associated with that button and the value is a valid QuickTips object.
25395      * For example:
25396 <pre><code>
25397 {
25398     bold : {
25399         title: 'Bold (Ctrl+B)',
25400         text: 'Make the selected text bold.',
25401         cls: 'x-html-editor-tip'
25402     },
25403     italic : {
25404         title: 'Italic (Ctrl+I)',
25405         text: 'Make the selected text italic.',
25406         cls: 'x-html-editor-tip'
25407     },
25408     ...
25409 </code></pre>
25410     * @type Object
25411      */
25412     buttonTips : {
25413         bold : {
25414             title: 'Bold (Ctrl+B)',
25415             text: 'Make the selected text bold.',
25416             cls: 'x-html-editor-tip'
25417         },
25418         italic : {
25419             title: 'Italic (Ctrl+I)',
25420             text: 'Make the selected text italic.',
25421             cls: 'x-html-editor-tip'
25422         },
25423         underline : {
25424             title: 'Underline (Ctrl+U)',
25425             text: 'Underline the selected text.',
25426             cls: 'x-html-editor-tip'
25427         },
25428         increasefontsize : {
25429             title: 'Grow Text',
25430             text: 'Increase the font size.',
25431             cls: 'x-html-editor-tip'
25432         },
25433         decreasefontsize : {
25434             title: 'Shrink Text',
25435             text: 'Decrease the font size.',
25436             cls: 'x-html-editor-tip'
25437         },
25438         backcolor : {
25439             title: 'Text Highlight Color',
25440             text: 'Change the background color of the selected text.',
25441             cls: 'x-html-editor-tip'
25442         },
25443         forecolor : {
25444             title: 'Font Color',
25445             text: 'Change the color of the selected text.',
25446             cls: 'x-html-editor-tip'
25447         },
25448         justifyleft : {
25449             title: 'Align Text Left',
25450             text: 'Align text to the left.',
25451             cls: 'x-html-editor-tip'
25452         },
25453         justifycenter : {
25454             title: 'Center Text',
25455             text: 'Center text in the editor.',
25456             cls: 'x-html-editor-tip'
25457         },
25458         justifyright : {
25459             title: 'Align Text Right',
25460             text: 'Align text to the right.',
25461             cls: 'x-html-editor-tip'
25462         },
25463         insertunorderedlist : {
25464             title: 'Bullet List',
25465             text: 'Start a bulleted list.',
25466             cls: 'x-html-editor-tip'
25467         },
25468         insertorderedlist : {
25469             title: 'Numbered List',
25470             text: 'Start a numbered list.',
25471             cls: 'x-html-editor-tip'
25472         },
25473         createlink : {
25474             title: 'Hyperlink',
25475             text: 'Make the selected text a hyperlink.',
25476             cls: 'x-html-editor-tip'
25477         },
25478         sourceedit : {
25479             title: 'Source Edit',
25480             text: 'Switch to source editing mode.',
25481             cls: 'x-html-editor-tip'
25482         }
25483     },
25484     // private
25485     onDestroy : function(){
25486         if(this.rendered){
25487             
25488             this.tb.items.each(function(item){
25489                 if(item.menu){
25490                     item.menu.removeAll();
25491                     if(item.menu.el){
25492                         item.menu.el.destroy();
25493                     }
25494                 }
25495                 item.destroy();
25496             });
25497              
25498         }
25499     },
25500     onFirstFocus: function() {
25501         this.tb.items.each(function(item){
25502            item.enable();
25503         });
25504     }
25505 });
25506
25507
25508
25509
25510 // <script type="text/javascript">
25511 /*
25512  * Based on
25513  * Ext JS Library 1.1.1
25514  * Copyright(c) 2006-2007, Ext JS, LLC.
25515  *  
25516  
25517  */
25518
25519  
25520 /**
25521  * @class Roo.form.HtmlEditor.ToolbarContext
25522  * Context Toolbar
25523  * 
25524  * Usage:
25525  *
25526  new Roo.form.HtmlEditor({
25527     ....
25528     toolbars : [
25529         new Roo.form.HtmlEditor.ToolbarStandard(),
25530         new Roo.form.HtmlEditor.ToolbarContext()
25531         })
25532     }
25533      
25534  * 
25535  * @config : {Object} disable List of elements to disable.. (not done yet.)
25536  * 
25537  * 
25538  */
25539
25540 Roo.form.HtmlEditor.ToolbarContext = function(config)
25541 {
25542     
25543     Roo.apply(this, config);
25544     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25545     // dont call parent... till later.
25546 }
25547 Roo.form.HtmlEditor.ToolbarContext.types = {
25548     'IMG' : {
25549         width : {
25550             title: "Width",
25551             width: 40
25552         },
25553         height:  {
25554             title: "Height",
25555             width: 40
25556         },
25557         align: {
25558             title: "Align",
25559             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
25560             width : 80
25561             
25562         },
25563         border: {
25564             title: "Border",
25565             width: 40
25566         },
25567         alt: {
25568             title: "Alt",
25569             width: 120
25570         },
25571         src : {
25572             title: "Src",
25573             width: 220
25574         }
25575         
25576     },
25577     'A' : {
25578         name : {
25579             title: "Name",
25580             width: 50
25581         },
25582         href:  {
25583             title: "Href",
25584             width: 220
25585         } // border?
25586         
25587     },
25588     'TABLE' : {
25589         rows : {
25590             title: "Rows",
25591             width: 20
25592         },
25593         cols : {
25594             title: "Cols",
25595             width: 20
25596         },
25597         width : {
25598             title: "Width",
25599             width: 40
25600         },
25601         height : {
25602             title: "Height",
25603             width: 40
25604         },
25605         border : {
25606             title: "Border",
25607             width: 20
25608         }
25609     },
25610     'TD' : {
25611         width : {
25612             title: "Width",
25613             width: 40
25614         },
25615         height : {
25616             title: "Height",
25617             width: 40
25618         },   
25619         align: {
25620             title: "Align",
25621             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
25622             width: 40
25623         },
25624         valign: {
25625             title: "Valign",
25626             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
25627             width: 40
25628         },
25629         colspan: {
25630             title: "Colspan",
25631             width: 20
25632             
25633         }
25634     },
25635     'INPUT' : {
25636         name : {
25637             title: "name",
25638             width: 120
25639         },
25640         value : {
25641             title: "Value",
25642             width: 120
25643         },
25644         width : {
25645             title: "Width",
25646             width: 40
25647         }
25648     },
25649     'LABEL' : {
25650         'for' : {
25651             title: "For",
25652             width: 120
25653         }
25654     },
25655     'TEXTAREA' : {
25656           name : {
25657             title: "name",
25658             width: 120
25659         },
25660         rows : {
25661             title: "Rows",
25662             width: 20
25663         },
25664         cols : {
25665             title: "Cols",
25666             width: 20
25667         }
25668     },
25669     'SELECT' : {
25670         name : {
25671             title: "name",
25672             width: 120
25673         },
25674         selectoptions : {
25675             title: "Options",
25676             width: 200
25677         }
25678     },
25679     'BODY' : {
25680         title : {
25681             title: "title",
25682             width: 120,
25683             disabled : true
25684         }
25685     }
25686 };
25687
25688
25689
25690 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
25691     
25692     tb: false,
25693     
25694     rendered: false,
25695     
25696     editor : false,
25697     /**
25698      * @cfg {Object} disable  List of toolbar elements to disable
25699          
25700      */
25701     disable : false,
25702     
25703     
25704     
25705     toolbars : false,
25706     
25707     init : function(editor)
25708     {
25709         this.editor = editor;
25710         
25711         
25712         var fid = editor.frameId;
25713         var etb = this;
25714         function btn(id, toggle, handler){
25715             var xid = fid + '-'+ id ;
25716             return {
25717                 id : xid,
25718                 cmd : id,
25719                 cls : 'x-btn-icon x-edit-'+id,
25720                 enableToggle:toggle !== false,
25721                 scope: editor, // was editor...
25722                 handler:handler||editor.relayBtnCmd,
25723                 clickEvent:'mousedown',
25724                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
25725                 tabIndex:-1
25726             };
25727         }
25728         // create a new element.
25729         var wdiv = editor.wrap.createChild({
25730                 tag: 'div'
25731             }, editor.wrap.dom.firstChild.nextSibling, true);
25732         
25733         // can we do this more than once??
25734         
25735          // stop form submits
25736       
25737  
25738         // disable everything...
25739         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
25740         this.toolbars = {};
25741            
25742         for (var i in  ty) {
25743           
25744             this.toolbars[i] = this.buildToolbar(ty[i],i);
25745         }
25746         this.tb = this.toolbars.BODY;
25747         this.tb.el.show();
25748         
25749          
25750         this.rendered = true;
25751         
25752         // the all the btns;
25753         editor.on('editorevent', this.updateToolbar, this);
25754         // other toolbars need to implement this..
25755         //editor.on('editmodechange', this.updateToolbar, this);
25756     },
25757     
25758     
25759     
25760     /**
25761      * Protected method that will not generally be called directly. It triggers
25762      * a toolbar update by reading the markup state of the current selection in the editor.
25763      */
25764     updateToolbar: function(){
25765
25766         if(!this.editor.activated){
25767             this.editor.onFirstFocus();
25768             return;
25769         }
25770
25771         
25772         var ans = this.editor.getAllAncestors();
25773         
25774         // pick
25775         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
25776         var sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
25777         sel = sel ? sel : this.editor.doc.body;
25778         sel = sel.tagName.length ? sel : this.editor.doc.body;
25779         var tn = sel.tagName.toUpperCase();
25780         sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
25781         tn = sel.tagName.toUpperCase();
25782         if (this.tb.name  == tn) {
25783             return; // no change
25784         }
25785         this.tb.el.hide();
25786         ///console.log("show: " + tn);
25787         this.tb =  this.toolbars[tn];
25788         this.tb.el.show();
25789         this.tb.fields.each(function(e) {
25790             e.setValue(sel.getAttribute(e.name));
25791         });
25792         this.tb.selectedNode = sel;
25793         
25794         
25795         Roo.menu.MenuMgr.hideAll();
25796
25797         //this.editorsyncValue();
25798     },
25799    
25800        
25801     // private
25802     onDestroy : function(){
25803         if(this.rendered){
25804             
25805             this.tb.items.each(function(item){
25806                 if(item.menu){
25807                     item.menu.removeAll();
25808                     if(item.menu.el){
25809                         item.menu.el.destroy();
25810                     }
25811                 }
25812                 item.destroy();
25813             });
25814              
25815         }
25816     },
25817     onFirstFocus: function() {
25818         // need to do this for all the toolbars..
25819         this.tb.items.each(function(item){
25820            item.enable();
25821         });
25822     },
25823     buildToolbar: function(tlist, nm)
25824     {
25825         var editor = this.editor;
25826          // create a new element.
25827         var wdiv = editor.wrap.createChild({
25828                 tag: 'div'
25829             }, editor.wrap.dom.firstChild.nextSibling, true);
25830         
25831        
25832         var tb = new Roo.Toolbar(wdiv);
25833         tb.add(nm+ ":&nbsp;");
25834         for (var i in tlist) {
25835             var item = tlist[i];
25836             tb.add(item.title + ":&nbsp;");
25837             if (item.opts) {
25838                 // fixme
25839                 
25840               
25841                 tb.addField( new Roo.form.ComboBox({
25842                     store: new Roo.data.SimpleStore({
25843                         id : 'val',
25844                         fields: ['val'],
25845                         data : item.opts // from states.js
25846                     }),
25847                     name : i,
25848                     displayField:'val',
25849                     typeAhead: false,
25850                     mode: 'local',
25851                     editable : false,
25852                     triggerAction: 'all',
25853                     emptyText:'Select',
25854                     selectOnFocus:true,
25855                     width: item.width ? item.width  : 130,
25856                     listeners : {
25857                         'select': function(c, r, i) {
25858                             tb.selectedNode.setAttribute(c.name, r.get('val'));
25859                         }
25860                     }
25861
25862                 }));
25863                 continue;
25864                     
25865                 
25866                 
25867                 
25868                 
25869                 tb.addField( new Roo.form.TextField({
25870                     name: i,
25871                     width: 100,
25872                     //allowBlank:false,
25873                     value: ''
25874                 }));
25875                 continue;
25876             }
25877             tb.addField( new Roo.form.TextField({
25878                 name: i,
25879                 width: item.width,
25880                 //allowBlank:true,
25881                 value: '',
25882                 listeners: {
25883                     'change' : function(f, nv, ov) {
25884                         tb.selectedNode.setAttribute(f.name, nv);
25885                     }
25886                 }
25887             }));
25888              
25889         }
25890         tb.el.on('click', function(e){
25891             e.preventDefault(); // what does this do?
25892         });
25893         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
25894         tb.el.hide();
25895         tb.name = nm;
25896         // dont need to disable them... as they will get hidden
25897         return tb;
25898          
25899         
25900     }
25901     
25902     
25903     
25904     
25905 });
25906
25907
25908
25909
25910
25911 /*
25912  * Based on:
25913  * Ext JS Library 1.1.1
25914  * Copyright(c) 2006-2007, Ext JS, LLC.
25915  *
25916  * Originally Released Under LGPL - original licence link has changed is not relivant.
25917  *
25918  * Fork - LGPL
25919  * <script type="text/javascript">
25920  */
25921  
25922 /**
25923  * @class Roo.form.BasicForm
25924  * @extends Roo.util.Observable
25925  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
25926  * @constructor
25927  * @param {String/HTMLElement/Roo.Element} el The form element or its id
25928  * @param {Object} config Configuration options
25929  */
25930 Roo.form.BasicForm = function(el, config){
25931     this.allItems = [];
25932     this.childForms = [];
25933     Roo.apply(this, config);
25934     /*
25935      * The Roo.form.Field items in this form.
25936      * @type MixedCollection
25937      */
25938      
25939      
25940     this.items = new Roo.util.MixedCollection(false, function(o){
25941         return o.id || (o.id = Roo.id());
25942     });
25943     this.addEvents({
25944         /**
25945          * @event beforeaction
25946          * Fires before any action is performed. Return false to cancel the action.
25947          * @param {Form} this
25948          * @param {Action} action The action to be performed
25949          */
25950         beforeaction: true,
25951         /**
25952          * @event actionfailed
25953          * Fires when an action fails.
25954          * @param {Form} this
25955          * @param {Action} action The action that failed
25956          */
25957         actionfailed : true,
25958         /**
25959          * @event actioncomplete
25960          * Fires when an action is completed.
25961          * @param {Form} this
25962          * @param {Action} action The action that completed
25963          */
25964         actioncomplete : true
25965     });
25966     if(el){
25967         this.initEl(el);
25968     }
25969     Roo.form.BasicForm.superclass.constructor.call(this);
25970 };
25971
25972 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
25973     /**
25974      * @cfg {String} method
25975      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
25976      */
25977     /**
25978      * @cfg {DataReader} reader
25979      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
25980      * This is optional as there is built-in support for processing JSON.
25981      */
25982     /**
25983      * @cfg {DataReader} errorReader
25984      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
25985      * This is completely optional as there is built-in support for processing JSON.
25986      */
25987     /**
25988      * @cfg {String} url
25989      * The URL to use for form actions if one isn't supplied in the action options.
25990      */
25991     /**
25992      * @cfg {Boolean} fileUpload
25993      * Set to true if this form is a file upload.
25994      */
25995      
25996     /**
25997      * @cfg {Object} baseParams
25998      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
25999      */
26000      /**
26001      
26002     /**
26003      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
26004      */
26005     timeout: 30,
26006
26007     // private
26008     activeAction : null,
26009
26010     /**
26011      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
26012      * or setValues() data instead of when the form was first created.
26013      */
26014     trackResetOnLoad : false,
26015     
26016     
26017     /**
26018      * childForms - used for multi-tab forms
26019      * @type {Array}
26020      */
26021     childForms : false,
26022     
26023     /**
26024      * allItems - full list of fields.
26025      * @type {Array}
26026      */
26027     allItems : false,
26028     
26029     /**
26030      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
26031      * element by passing it or its id or mask the form itself by passing in true.
26032      * @type Mixed
26033      */
26034     waitMsgTarget : false,
26035
26036     // private
26037     initEl : function(el){
26038         this.el = Roo.get(el);
26039         this.id = this.el.id || Roo.id();
26040         this.el.on('submit', this.onSubmit, this);
26041         this.el.addClass('x-form');
26042     },
26043
26044     // private
26045     onSubmit : function(e){
26046         e.stopEvent();
26047     },
26048
26049     /**
26050      * Returns true if client-side validation on the form is successful.
26051      * @return Boolean
26052      */
26053     isValid : function(){
26054         var valid = true;
26055         this.items.each(function(f){
26056            if(!f.validate()){
26057                valid = false;
26058            }
26059         });
26060         return valid;
26061     },
26062
26063     /**
26064      * Returns true if any fields in this form have changed since their original load.
26065      * @return Boolean
26066      */
26067     isDirty : function(){
26068         var dirty = false;
26069         this.items.each(function(f){
26070            if(f.isDirty()){
26071                dirty = true;
26072                return false;
26073            }
26074         });
26075         return dirty;
26076     },
26077
26078     /**
26079      * Performs a predefined action (submit or load) or custom actions you define on this form.
26080      * @param {String} actionName The name of the action type
26081      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
26082      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
26083      * accept other config options):
26084      * <pre>
26085 Property          Type             Description
26086 ----------------  ---------------  ----------------------------------------------------------------------------------
26087 url               String           The url for the action (defaults to the form's url)
26088 method            String           The form method to use (defaults to the form's method, or POST if not defined)
26089 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
26090 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
26091                                    validate the form on the client (defaults to false)
26092      * </pre>
26093      * @return {BasicForm} this
26094      */
26095     doAction : function(action, options){
26096         if(typeof action == 'string'){
26097             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
26098         }
26099         if(this.fireEvent('beforeaction', this, action) !== false){
26100             this.beforeAction(action);
26101             action.run.defer(100, action);
26102         }
26103         return this;
26104     },
26105
26106     /**
26107      * Shortcut to do a submit action.
26108      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26109      * @return {BasicForm} this
26110      */
26111     submit : function(options){
26112         this.doAction('submit', options);
26113         return this;
26114     },
26115
26116     /**
26117      * Shortcut to do a load action.
26118      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26119      * @return {BasicForm} this
26120      */
26121     load : function(options){
26122         this.doAction('load', options);
26123         return this;
26124     },
26125
26126     /**
26127      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
26128      * @param {Record} record The record to edit
26129      * @return {BasicForm} this
26130      */
26131     updateRecord : function(record){
26132         record.beginEdit();
26133         var fs = record.fields;
26134         fs.each(function(f){
26135             var field = this.findField(f.name);
26136             if(field){
26137                 record.set(f.name, field.getValue());
26138             }
26139         }, this);
26140         record.endEdit();
26141         return this;
26142     },
26143
26144     /**
26145      * Loads an Roo.data.Record into this form.
26146      * @param {Record} record The record to load
26147      * @return {BasicForm} this
26148      */
26149     loadRecord : function(record){
26150         this.setValues(record.data);
26151         return this;
26152     },
26153
26154     // private
26155     beforeAction : function(action){
26156         var o = action.options;
26157         
26158        
26159         if(this.waitMsgTarget === true){
26160             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
26161         }else if(this.waitMsgTarget){
26162             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
26163             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
26164         }else {
26165             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
26166         }
26167          
26168     },
26169
26170     // private
26171     afterAction : function(action, success){
26172         this.activeAction = null;
26173         var o = action.options;
26174         
26175         if(this.waitMsgTarget === true){
26176             this.el.unmask();
26177         }else if(this.waitMsgTarget){
26178             this.waitMsgTarget.unmask();
26179         }else{
26180             Roo.MessageBox.updateProgress(1);
26181             Roo.MessageBox.hide();
26182         }
26183          
26184         if(success){
26185             if(o.reset){
26186                 this.reset();
26187             }
26188             Roo.callback(o.success, o.scope, [this, action]);
26189             this.fireEvent('actioncomplete', this, action);
26190             
26191         }else{
26192             Roo.callback(o.failure, o.scope, [this, action]);
26193             // show an error message if no failed handler is set..
26194             if (!this.hasListener('actionfailed')) {
26195                 Roo.MessageBox.alert("Error", "Saving Failed, please check your entries");
26196             }
26197             
26198             this.fireEvent('actionfailed', this, action);
26199         }
26200         
26201     },
26202
26203     /**
26204      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
26205      * @param {String} id The value to search for
26206      * @return Field
26207      */
26208     findField : function(id){
26209         var field = this.items.get(id);
26210         if(!field){
26211             this.items.each(function(f){
26212                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
26213                     field = f;
26214                     return false;
26215                 }
26216             });
26217         }
26218         return field || null;
26219     },
26220
26221     /**
26222      * Add a secondary form to this one, 
26223      * Used to provide tabbed forms. One form is primary, with hidden values 
26224      * which mirror the elements from the other forms.
26225      * 
26226      * @param {Roo.form.Form} form to add.
26227      * 
26228      */
26229     addForm : function(form)
26230     {
26231        
26232         if (this.childForms.indexOf(form) > -1) {
26233             // already added..
26234             return;
26235         }
26236         this.childForms.push(form);
26237         var n = '';
26238         Roo.each(form.allItems, function (fe) {
26239             
26240             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
26241             if (this.findField(n)) { // already added..
26242                 return;
26243             }
26244             var add = new Roo.form.Hidden({
26245                 name : n
26246             });
26247             add.render(this.el);
26248             
26249             this.add( add );
26250         }, this);
26251         
26252     },
26253     /**
26254      * Mark fields in this form invalid in bulk.
26255      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
26256      * @return {BasicForm} this
26257      */
26258     markInvalid : function(errors){
26259         if(errors instanceof Array){
26260             for(var i = 0, len = errors.length; i < len; i++){
26261                 var fieldError = errors[i];
26262                 var f = this.findField(fieldError.id);
26263                 if(f){
26264                     f.markInvalid(fieldError.msg);
26265                 }
26266             }
26267         }else{
26268             var field, id;
26269             for(id in errors){
26270                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
26271                     field.markInvalid(errors[id]);
26272                 }
26273             }
26274         }
26275         Roo.each(this.childForms || [], function (f) {
26276             f.markInvalid(errors);
26277         });
26278         
26279         return this;
26280     },
26281
26282     /**
26283      * Set values for fields in this form in bulk.
26284      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
26285      * @return {BasicForm} this
26286      */
26287     setValues : function(values){
26288         if(values instanceof Array){ // array of objects
26289             for(var i = 0, len = values.length; i < len; i++){
26290                 var v = values[i];
26291                 var f = this.findField(v.id);
26292                 if(f){
26293                     f.setValue(v.value);
26294                     if(this.trackResetOnLoad){
26295                         f.originalValue = f.getValue();
26296                     }
26297                 }
26298             }
26299         }else{ // object hash
26300             var field, id;
26301             for(id in values){
26302                 if(typeof values[id] != 'function' && (field = this.findField(id))){
26303                     
26304                     if (field.setFromData && 
26305                         field.valueField && 
26306                         field.displayField &&
26307                         // combos' with local stores can 
26308                         // be queried via setValue()
26309                         // to set their value..
26310                         (field.store && !field.store.isLocal)
26311                         ) {
26312                         // it's a combo
26313                         var sd = { };
26314                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
26315                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
26316                         field.setFromData(sd);
26317                         
26318                     } else {
26319                         field.setValue(values[id]);
26320                     }
26321                     
26322                     
26323                     if(this.trackResetOnLoad){
26324                         field.originalValue = field.getValue();
26325                     }
26326                 }
26327             }
26328         }
26329          
26330         Roo.each(this.childForms || [], function (f) {
26331             f.setValues(values);
26332         });
26333                 
26334         return this;
26335     },
26336
26337     /**
26338      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
26339      * they are returned as an array.
26340      * @param {Boolean} asString
26341      * @return {Object}
26342      */
26343     getValues : function(asString){
26344         if (this.childForms) {
26345             // copy values from the child forms
26346             Roo.each(this.childForms, function (f) {
26347                 this.setValues(f.getValues());
26348             }, this);
26349         }
26350         
26351         
26352         
26353         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
26354         if(asString === true){
26355             return fs;
26356         }
26357         return Roo.urlDecode(fs);
26358     },
26359     
26360     /**
26361      * Returns the fields in this form as an object with key/value pairs. 
26362      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
26363      * @return {Object}
26364      */
26365     getFieldValues : function()
26366     {
26367         if (this.childForms) {
26368             // copy values from the child forms
26369             Roo.each(this.childForms, function (f) {
26370                 this.setValues(f.getValues());
26371             }, this);
26372         }
26373         
26374         var ret = {};
26375         this.items.each(function(f){
26376             if (!f.getName()) {
26377                 return;
26378             }
26379             var v = f.getValue();
26380             if ((typeof(v) == 'object') && f.getRawValue) {
26381                 v = f.getRawValue() ; // dates..
26382             }
26383             ret[f.getName()] = v;
26384         });
26385         
26386         return ret;
26387     },
26388
26389     /**
26390      * Clears all invalid messages in this form.
26391      * @return {BasicForm} this
26392      */
26393     clearInvalid : function(){
26394         this.items.each(function(f){
26395            f.clearInvalid();
26396         });
26397         
26398         Roo.each(this.childForms || [], function (f) {
26399             f.clearInvalid();
26400         });
26401         
26402         
26403         return this;
26404     },
26405
26406     /**
26407      * Resets this form.
26408      * @return {BasicForm} this
26409      */
26410     reset : function(){
26411         this.items.each(function(f){
26412             f.reset();
26413         });
26414         
26415         Roo.each(this.childForms || [], function (f) {
26416             f.reset();
26417         });
26418        
26419         
26420         return this;
26421     },
26422
26423     /**
26424      * Add Roo.form components to this form.
26425      * @param {Field} field1
26426      * @param {Field} field2 (optional)
26427      * @param {Field} etc (optional)
26428      * @return {BasicForm} this
26429      */
26430     add : function(){
26431         this.items.addAll(Array.prototype.slice.call(arguments, 0));
26432         return this;
26433     },
26434
26435
26436     /**
26437      * Removes a field from the items collection (does NOT remove its markup).
26438      * @param {Field} field
26439      * @return {BasicForm} this
26440      */
26441     remove : function(field){
26442         this.items.remove(field);
26443         return this;
26444     },
26445
26446     /**
26447      * Looks at the fields in this form, checks them for an id attribute,
26448      * and calls applyTo on the existing dom element with that id.
26449      * @return {BasicForm} this
26450      */
26451     render : function(){
26452         this.items.each(function(f){
26453             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
26454                 f.applyTo(f.id);
26455             }
26456         });
26457         return this;
26458     },
26459
26460     /**
26461      * Calls {@link Ext#apply} for all fields in this form with the passed object.
26462      * @param {Object} values
26463      * @return {BasicForm} this
26464      */
26465     applyToFields : function(o){
26466         this.items.each(function(f){
26467            Roo.apply(f, o);
26468         });
26469         return this;
26470     },
26471
26472     /**
26473      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
26474      * @param {Object} values
26475      * @return {BasicForm} this
26476      */
26477     applyIfToFields : function(o){
26478         this.items.each(function(f){
26479            Roo.applyIf(f, o);
26480         });
26481         return this;
26482     }
26483 });
26484
26485 // back compat
26486 Roo.BasicForm = Roo.form.BasicForm;/*
26487  * Based on:
26488  * Ext JS Library 1.1.1
26489  * Copyright(c) 2006-2007, Ext JS, LLC.
26490  *
26491  * Originally Released Under LGPL - original licence link has changed is not relivant.
26492  *
26493  * Fork - LGPL
26494  * <script type="text/javascript">
26495  */
26496
26497 /**
26498  * @class Roo.form.Form
26499  * @extends Roo.form.BasicForm
26500  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
26501  * @constructor
26502  * @param {Object} config Configuration options
26503  */
26504 Roo.form.Form = function(config){
26505     var xitems =  [];
26506     if (config.items) {
26507         xitems = config.items;
26508         delete config.items;
26509     }
26510    
26511     
26512     Roo.form.Form.superclass.constructor.call(this, null, config);
26513     this.url = this.url || this.action;
26514     if(!this.root){
26515         this.root = new Roo.form.Layout(Roo.applyIf({
26516             id: Roo.id()
26517         }, config));
26518     }
26519     this.active = this.root;
26520     /**
26521      * Array of all the buttons that have been added to this form via {@link addButton}
26522      * @type Array
26523      */
26524     this.buttons = [];
26525     this.allItems = [];
26526     this.addEvents({
26527         /**
26528          * @event clientvalidation
26529          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
26530          * @param {Form} this
26531          * @param {Boolean} valid true if the form has passed client-side validation
26532          */
26533         clientvalidation: true,
26534         /**
26535          * @event rendered
26536          * Fires when the form is rendered
26537          * @param {Roo.form.Form} form
26538          */
26539         rendered : true
26540     });
26541     
26542     if (this.progressUrl) {
26543             // push a hidden field onto the list of fields..
26544             this.addxtype( {
26545                     xns: Roo.form, 
26546                     xtype : 'Hidden', 
26547                     name : 'UPLOAD_IDENTIFIER' 
26548             });
26549         }
26550         
26551     
26552     Roo.each(xitems, this.addxtype, this);
26553     
26554     
26555     
26556 };
26557
26558 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
26559     /**
26560      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
26561      */
26562     /**
26563      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
26564      */
26565     /**
26566      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
26567      */
26568     buttonAlign:'center',
26569
26570     /**
26571      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
26572      */
26573     minButtonWidth:75,
26574
26575     /**
26576      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
26577      * This property cascades to child containers if not set.
26578      */
26579     labelAlign:'left',
26580
26581     /**
26582      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
26583      * fires a looping event with that state. This is required to bind buttons to the valid
26584      * state using the config value formBind:true on the button.
26585      */
26586     monitorValid : false,
26587
26588     /**
26589      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
26590      */
26591     monitorPoll : 200,
26592     
26593     /**
26594      * @cfg {String} progressUrl - Url to return progress data 
26595      */
26596     
26597     progressUrl : false,
26598   
26599     /**
26600      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
26601      * fields are added and the column is closed. If no fields are passed the column remains open
26602      * until end() is called.
26603      * @param {Object} config The config to pass to the column
26604      * @param {Field} field1 (optional)
26605      * @param {Field} field2 (optional)
26606      * @param {Field} etc (optional)
26607      * @return Column The column container object
26608      */
26609     column : function(c){
26610         var col = new Roo.form.Column(c);
26611         this.start(col);
26612         if(arguments.length > 1){ // duplicate code required because of Opera
26613             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26614             this.end();
26615         }
26616         return col;
26617     },
26618
26619     /**
26620      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
26621      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
26622      * until end() is called.
26623      * @param {Object} config The config to pass to the fieldset
26624      * @param {Field} field1 (optional)
26625      * @param {Field} field2 (optional)
26626      * @param {Field} etc (optional)
26627      * @return FieldSet The fieldset container object
26628      */
26629     fieldset : function(c){
26630         var fs = new Roo.form.FieldSet(c);
26631         this.start(fs);
26632         if(arguments.length > 1){ // duplicate code required because of Opera
26633             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26634             this.end();
26635         }
26636         return fs;
26637     },
26638
26639     /**
26640      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
26641      * fields are added and the container is closed. If no fields are passed the container remains open
26642      * until end() is called.
26643      * @param {Object} config The config to pass to the Layout
26644      * @param {Field} field1 (optional)
26645      * @param {Field} field2 (optional)
26646      * @param {Field} etc (optional)
26647      * @return Layout The container object
26648      */
26649     container : function(c){
26650         var l = new Roo.form.Layout(c);
26651         this.start(l);
26652         if(arguments.length > 1){ // duplicate code required because of Opera
26653             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26654             this.end();
26655         }
26656         return l;
26657     },
26658
26659     /**
26660      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
26661      * @param {Object} container A Roo.form.Layout or subclass of Layout
26662      * @return {Form} this
26663      */
26664     start : function(c){
26665         // cascade label info
26666         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
26667         this.active.stack.push(c);
26668         c.ownerCt = this.active;
26669         this.active = c;
26670         return this;
26671     },
26672
26673     /**
26674      * Closes the current open container
26675      * @return {Form} this
26676      */
26677     end : function(){
26678         if(this.active == this.root){
26679             return this;
26680         }
26681         this.active = this.active.ownerCt;
26682         return this;
26683     },
26684
26685     /**
26686      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
26687      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
26688      * as the label of the field.
26689      * @param {Field} field1
26690      * @param {Field} field2 (optional)
26691      * @param {Field} etc. (optional)
26692      * @return {Form} this
26693      */
26694     add : function(){
26695         this.active.stack.push.apply(this.active.stack, arguments);
26696         this.allItems.push.apply(this.allItems,arguments);
26697         var r = [];
26698         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
26699             if(a[i].isFormField){
26700                 r.push(a[i]);
26701             }
26702         }
26703         if(r.length > 0){
26704             Roo.form.Form.superclass.add.apply(this, r);
26705         }
26706         return this;
26707     },
26708     
26709
26710     
26711     
26712     
26713      /**
26714      * Find any element that has been added to a form, using it's ID or name
26715      * This can include framesets, columns etc. along with regular fields..
26716      * @param {String} id - id or name to find.
26717      
26718      * @return {Element} e - or false if nothing found.
26719      */
26720     findbyId : function(id)
26721     {
26722         var ret = false;
26723         if (!id) {
26724             return ret;
26725         }
26726         Ext.each(this.allItems, function(f){
26727             if (f.id == id || f.name == id ){
26728                 ret = f;
26729                 return false;
26730             }
26731         });
26732         return ret;
26733     },
26734
26735     
26736     
26737     /**
26738      * Render this form into the passed container. This should only be called once!
26739      * @param {String/HTMLElement/Element} container The element this component should be rendered into
26740      * @return {Form} this
26741      */
26742     render : function(ct)
26743     {
26744         
26745         
26746         
26747         ct = Roo.get(ct);
26748         var o = this.autoCreate || {
26749             tag: 'form',
26750             method : this.method || 'POST',
26751             id : this.id || Roo.id()
26752         };
26753         this.initEl(ct.createChild(o));
26754
26755         this.root.render(this.el);
26756         
26757        
26758              
26759         this.items.each(function(f){
26760             f.render('x-form-el-'+f.id);
26761         });
26762
26763         if(this.buttons.length > 0){
26764             // tables are required to maintain order and for correct IE layout
26765             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
26766                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
26767                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
26768             }}, null, true);
26769             var tr = tb.getElementsByTagName('tr')[0];
26770             for(var i = 0, len = this.buttons.length; i < len; i++) {
26771                 var b = this.buttons[i];
26772                 var td = document.createElement('td');
26773                 td.className = 'x-form-btn-td';
26774                 b.render(tr.appendChild(td));
26775             }
26776         }
26777         if(this.monitorValid){ // initialize after render
26778             this.startMonitoring();
26779         }
26780         this.fireEvent('rendered', this);
26781         return this;
26782     },
26783
26784     /**
26785      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
26786      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
26787      * object or a valid Roo.DomHelper element config
26788      * @param {Function} handler The function called when the button is clicked
26789      * @param {Object} scope (optional) The scope of the handler function
26790      * @return {Roo.Button}
26791      */
26792     addButton : function(config, handler, scope){
26793         var bc = {
26794             handler: handler,
26795             scope: scope,
26796             minWidth: this.minButtonWidth,
26797             hideParent:true
26798         };
26799         if(typeof config == "string"){
26800             bc.text = config;
26801         }else{
26802             Roo.apply(bc, config);
26803         }
26804         var btn = new Roo.Button(null, bc);
26805         this.buttons.push(btn);
26806         return btn;
26807     },
26808
26809      /**
26810      * Adds a series of form elements (using the xtype property as the factory method.
26811      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
26812      * @param {Object} config 
26813      */
26814     
26815     addxtype : function()
26816     {
26817         var ar = Array.prototype.slice.call(arguments, 0);
26818         var ret = false;
26819         for(var i = 0; i < ar.length; i++) {
26820             if (!ar[i]) {
26821                 continue; // skip -- if this happends something invalid got sent, we 
26822                 // should ignore it, as basically that interface element will not show up
26823                 // and that should be pretty obvious!!
26824             }
26825             
26826             if (Roo.form[ar[i].xtype]) {
26827                 ar[i].form = this;
26828                 var fe = Roo.factory(ar[i], Roo.form);
26829                 if (!ret) {
26830                     ret = fe;
26831                 }
26832                 fe.form = this;
26833                 if (fe.store) {
26834                     fe.store.form = this;
26835                 }
26836                 if (fe.isLayout) {  
26837                          
26838                     this.start(fe);
26839                     this.allItems.push(fe);
26840                     if (fe.items && fe.addxtype) {
26841                         fe.addxtype.apply(fe, fe.items);
26842                         delete fe.items;
26843                     }
26844                      this.end();
26845                     continue;
26846                 }
26847                 
26848                 
26849                  
26850                 this.add(fe);
26851               //  console.log('adding ' + ar[i].xtype);
26852             }
26853             if (ar[i].xtype == 'Button') {  
26854                 //console.log('adding button');
26855                 //console.log(ar[i]);
26856                 this.addButton(ar[i]);
26857                 this.allItems.push(fe);
26858                 continue;
26859             }
26860             
26861             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
26862                 alert('end is not supported on xtype any more, use items');
26863             //    this.end();
26864             //    //console.log('adding end');
26865             }
26866             
26867         }
26868         return ret;
26869     },
26870     
26871     /**
26872      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
26873      * option "monitorValid"
26874      */
26875     startMonitoring : function(){
26876         if(!this.bound){
26877             this.bound = true;
26878             Roo.TaskMgr.start({
26879                 run : this.bindHandler,
26880                 interval : this.monitorPoll || 200,
26881                 scope: this
26882             });
26883         }
26884     },
26885
26886     /**
26887      * Stops monitoring of the valid state of this form
26888      */
26889     stopMonitoring : function(){
26890         this.bound = false;
26891     },
26892
26893     // private
26894     bindHandler : function(){
26895         if(!this.bound){
26896             return false; // stops binding
26897         }
26898         var valid = true;
26899         this.items.each(function(f){
26900             if(!f.isValid(true)){
26901                 valid = false;
26902                 return false;
26903             }
26904         });
26905         for(var i = 0, len = this.buttons.length; i < len; i++){
26906             var btn = this.buttons[i];
26907             if(btn.formBind === true && btn.disabled === valid){
26908                 btn.setDisabled(!valid);
26909             }
26910         }
26911         this.fireEvent('clientvalidation', this, valid);
26912     }
26913     
26914     
26915     
26916     
26917     
26918     
26919     
26920     
26921 });
26922
26923
26924 // back compat
26925 Roo.Form = Roo.form.Form;
26926 /*
26927  * Based on:
26928  * Ext JS Library 1.1.1
26929  * Copyright(c) 2006-2007, Ext JS, LLC.
26930  *
26931  * Originally Released Under LGPL - original licence link has changed is not relivant.
26932  *
26933  * Fork - LGPL
26934  * <script type="text/javascript">
26935  */
26936  
26937  /**
26938  * @class Roo.form.Action
26939  * Internal Class used to handle form actions
26940  * @constructor
26941  * @param {Roo.form.BasicForm} el The form element or its id
26942  * @param {Object} config Configuration options
26943  */
26944  
26945  
26946 // define the action interface
26947 Roo.form.Action = function(form, options){
26948     this.form = form;
26949     this.options = options || {};
26950 };
26951 /**
26952  * Client Validation Failed
26953  * @const 
26954  */
26955 Roo.form.Action.CLIENT_INVALID = 'client';
26956 /**
26957  * Server Validation Failed
26958  * @const 
26959  */
26960  Roo.form.Action.SERVER_INVALID = 'server';
26961  /**
26962  * Connect to Server Failed
26963  * @const 
26964  */
26965 Roo.form.Action.CONNECT_FAILURE = 'connect';
26966 /**
26967  * Reading Data from Server Failed
26968  * @const 
26969  */
26970 Roo.form.Action.LOAD_FAILURE = 'load';
26971
26972 Roo.form.Action.prototype = {
26973     type : 'default',
26974     failureType : undefined,
26975     response : undefined,
26976     result : undefined,
26977
26978     // interface method
26979     run : function(options){
26980
26981     },
26982
26983     // interface method
26984     success : function(response){
26985
26986     },
26987
26988     // interface method
26989     handleResponse : function(response){
26990
26991     },
26992
26993     // default connection failure
26994     failure : function(response){
26995         
26996         this.response = response;
26997         this.failureType = Roo.form.Action.CONNECT_FAILURE;
26998         this.form.afterAction(this, false);
26999     },
27000
27001     processResponse : function(response){
27002         this.response = response;
27003         if(!response.responseText){
27004             return true;
27005         }
27006         this.result = this.handleResponse(response);
27007         return this.result;
27008     },
27009
27010     // utility functions used internally
27011     getUrl : function(appendParams){
27012         var url = this.options.url || this.form.url || this.form.el.dom.action;
27013         if(appendParams){
27014             var p = this.getParams();
27015             if(p){
27016                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
27017             }
27018         }
27019         return url;
27020     },
27021
27022     getMethod : function(){
27023         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
27024     },
27025
27026     getParams : function(){
27027         var bp = this.form.baseParams;
27028         var p = this.options.params;
27029         if(p){
27030             if(typeof p == "object"){
27031                 p = Roo.urlEncode(Roo.applyIf(p, bp));
27032             }else if(typeof p == 'string' && bp){
27033                 p += '&' + Roo.urlEncode(bp);
27034             }
27035         }else if(bp){
27036             p = Roo.urlEncode(bp);
27037         }
27038         return p;
27039     },
27040
27041     createCallback : function(){
27042         return {
27043             success: this.success,
27044             failure: this.failure,
27045             scope: this,
27046             timeout: (this.form.timeout*1000),
27047             upload: this.form.fileUpload ? this.success : undefined
27048         };
27049     }
27050 };
27051
27052 Roo.form.Action.Submit = function(form, options){
27053     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
27054 };
27055
27056 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
27057     type : 'submit',
27058
27059     haveProgress : false,
27060     uploadComplete : false,
27061     
27062     // uploadProgress indicator.
27063     uploadProgress : function()
27064     {
27065         if (!this.form.progressUrl) {
27066             return;
27067         }
27068         
27069         if (!this.haveProgress) {
27070             Roo.MessageBox.progress("Uploading", "Uploading");
27071         }
27072         if (this.uploadComplete) {
27073            Roo.MessageBox.hide();
27074            return;
27075         }
27076         
27077         this.haveProgress = true;
27078    
27079         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
27080         
27081         var c = new Roo.data.Connection();
27082         c.request({
27083             url : this.form.progressUrl,
27084             params: {
27085                 id : uid
27086             },
27087             method: 'GET',
27088             success : function(req){
27089                //console.log(data);
27090                 var rdata = false;
27091                 var edata;
27092                 try  {
27093                    rdata = Roo.decode(req.responseText)
27094                 } catch (e) {
27095                     Roo.log("Invalid data from server..");
27096                     Roo.log(edata);
27097                     return;
27098                 }
27099                 if (!rdata || !rdata.success) {
27100                     Roo.log(rdata);
27101                     return;
27102                 }
27103                 var data = rdata.data;
27104                 
27105                 if (this.uploadComplete) {
27106                    Roo.MessageBox.hide();
27107                    return;
27108                 }
27109                    
27110                 if (data){
27111                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
27112                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
27113                     );
27114                 }
27115                 this.uploadProgress.defer(2000,this);
27116             },
27117        
27118             failure: function(data) {
27119                 Roo.log('progress url failed ');
27120                 Roo.log(data);
27121             },
27122             scope : this
27123         });
27124            
27125     },
27126     
27127     
27128     run : function()
27129     {
27130         // run get Values on the form, so it syncs any secondary forms.
27131         this.form.getValues();
27132         
27133         var o = this.options;
27134         var method = this.getMethod();
27135         var isPost = method == 'POST';
27136         if(o.clientValidation === false || this.form.isValid()){
27137             
27138             if (this.form.progressUrl) {
27139                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
27140                     (new Date() * 1) + '' + Math.random());
27141                     
27142             } 
27143             
27144             
27145             Roo.Ajax.request(Roo.apply(this.createCallback(), {
27146                 form:this.form.el.dom,
27147                 url:this.getUrl(!isPost),
27148                 method: method,
27149                 params:isPost ? this.getParams() : null,
27150                 isUpload: this.form.fileUpload
27151             }));
27152             
27153             this.uploadProgress();
27154
27155         }else if (o.clientValidation !== false){ // client validation failed
27156             this.failureType = Roo.form.Action.CLIENT_INVALID;
27157             this.form.afterAction(this, false);
27158         }
27159     },
27160
27161     success : function(response)
27162     {
27163         this.uploadComplete= true;
27164         if (this.haveProgress) {
27165             Roo.MessageBox.hide();
27166         }
27167         
27168         
27169         var result = this.processResponse(response);
27170         if(result === true || result.success){
27171             this.form.afterAction(this, true);
27172             return;
27173         }
27174         if(result.errors){
27175             this.form.markInvalid(result.errors);
27176             this.failureType = Roo.form.Action.SERVER_INVALID;
27177         }
27178         this.form.afterAction(this, false);
27179     },
27180     failure : function(response)
27181     {
27182         this.uploadComplete= true;
27183         if (this.haveProgress) {
27184             Roo.MessageBox.hide();
27185         }
27186         
27187         
27188         this.response = response;
27189         this.failureType = Roo.form.Action.CONNECT_FAILURE;
27190         this.form.afterAction(this, false);
27191     },
27192     
27193     handleResponse : function(response){
27194         if(this.form.errorReader){
27195             var rs = this.form.errorReader.read(response);
27196             var errors = [];
27197             if(rs.records){
27198                 for(var i = 0, len = rs.records.length; i < len; i++) {
27199                     var r = rs.records[i];
27200                     errors[i] = r.data;
27201                 }
27202             }
27203             if(errors.length < 1){
27204                 errors = null;
27205             }
27206             return {
27207                 success : rs.success,
27208                 errors : errors
27209             };
27210         }
27211         var ret = false;
27212         try {
27213             ret = Roo.decode(response.responseText);
27214         } catch (e) {
27215             ret = {
27216                 success: false,
27217                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
27218                 errors : []
27219             };
27220         }
27221         return ret;
27222         
27223     }
27224 });
27225
27226
27227 Roo.form.Action.Load = function(form, options){
27228     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
27229     this.reader = this.form.reader;
27230 };
27231
27232 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
27233     type : 'load',
27234
27235     run : function(){
27236         
27237         Roo.Ajax.request(Roo.apply(
27238                 this.createCallback(), {
27239                     method:this.getMethod(),
27240                     url:this.getUrl(false),
27241                     params:this.getParams()
27242         }));
27243     },
27244
27245     success : function(response){
27246         
27247         var result = this.processResponse(response);
27248         if(result === true || !result.success || !result.data){
27249             this.failureType = Roo.form.Action.LOAD_FAILURE;
27250             this.form.afterAction(this, false);
27251             return;
27252         }
27253         this.form.clearInvalid();
27254         this.form.setValues(result.data);
27255         this.form.afterAction(this, true);
27256     },
27257
27258     handleResponse : function(response){
27259         if(this.form.reader){
27260             var rs = this.form.reader.read(response);
27261             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
27262             return {
27263                 success : rs.success,
27264                 data : data
27265             };
27266         }
27267         return Roo.decode(response.responseText);
27268     }
27269 });
27270
27271 Roo.form.Action.ACTION_TYPES = {
27272     'load' : Roo.form.Action.Load,
27273     'submit' : Roo.form.Action.Submit
27274 };/*
27275  * Based on:
27276  * Ext JS Library 1.1.1
27277  * Copyright(c) 2006-2007, Ext JS, LLC.
27278  *
27279  * Originally Released Under LGPL - original licence link has changed is not relivant.
27280  *
27281  * Fork - LGPL
27282  * <script type="text/javascript">
27283  */
27284  
27285 /**
27286  * @class Roo.form.Layout
27287  * @extends Roo.Component
27288  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
27289  * @constructor
27290  * @param {Object} config Configuration options
27291  */
27292 Roo.form.Layout = function(config){
27293     var xitems = [];
27294     if (config.items) {
27295         xitems = config.items;
27296         delete config.items;
27297     }
27298     Roo.form.Layout.superclass.constructor.call(this, config);
27299     this.stack = [];
27300     Roo.each(xitems, this.addxtype, this);
27301      
27302 };
27303
27304 Roo.extend(Roo.form.Layout, Roo.Component, {
27305     /**
27306      * @cfg {String/Object} autoCreate
27307      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
27308      */
27309     /**
27310      * @cfg {String/Object/Function} style
27311      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
27312      * a function which returns such a specification.
27313      */
27314     /**
27315      * @cfg {String} labelAlign
27316      * Valid values are "left," "top" and "right" (defaults to "left")
27317      */
27318     /**
27319      * @cfg {Number} labelWidth
27320      * Fixed width in pixels of all field labels (defaults to undefined)
27321      */
27322     /**
27323      * @cfg {Boolean} clear
27324      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
27325      */
27326     clear : true,
27327     /**
27328      * @cfg {String} labelSeparator
27329      * The separator to use after field labels (defaults to ':')
27330      */
27331     labelSeparator : ':',
27332     /**
27333      * @cfg {Boolean} hideLabels
27334      * True to suppress the display of field labels in this layout (defaults to false)
27335      */
27336     hideLabels : false,
27337
27338     // private
27339     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
27340     
27341     isLayout : true,
27342     
27343     // private
27344     onRender : function(ct, position){
27345         if(this.el){ // from markup
27346             this.el = Roo.get(this.el);
27347         }else {  // generate
27348             var cfg = this.getAutoCreate();
27349             this.el = ct.createChild(cfg, position);
27350         }
27351         if(this.style){
27352             this.el.applyStyles(this.style);
27353         }
27354         if(this.labelAlign){
27355             this.el.addClass('x-form-label-'+this.labelAlign);
27356         }
27357         if(this.hideLabels){
27358             this.labelStyle = "display:none";
27359             this.elementStyle = "padding-left:0;";
27360         }else{
27361             if(typeof this.labelWidth == 'number'){
27362                 this.labelStyle = "width:"+this.labelWidth+"px;";
27363                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
27364             }
27365             if(this.labelAlign == 'top'){
27366                 this.labelStyle = "width:auto;";
27367                 this.elementStyle = "padding-left:0;";
27368             }
27369         }
27370         var stack = this.stack;
27371         var slen = stack.length;
27372         if(slen > 0){
27373             if(!this.fieldTpl){
27374                 var t = new Roo.Template(
27375                     '<div class="x-form-item {5}">',
27376                         '<label for="{0}" style="{2}">{1}{4}</label>',
27377                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
27378                         '</div>',
27379                     '</div><div class="x-form-clear-left"></div>'
27380                 );
27381                 t.disableFormats = true;
27382                 t.compile();
27383                 Roo.form.Layout.prototype.fieldTpl = t;
27384             }
27385             for(var i = 0; i < slen; i++) {
27386                 if(stack[i].isFormField){
27387                     this.renderField(stack[i]);
27388                 }else{
27389                     this.renderComponent(stack[i]);
27390                 }
27391             }
27392         }
27393         if(this.clear){
27394             this.el.createChild({cls:'x-form-clear'});
27395         }
27396     },
27397
27398     // private
27399     renderField : function(f){
27400         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
27401                f.id, //0
27402                f.fieldLabel, //1
27403                f.labelStyle||this.labelStyle||'', //2
27404                this.elementStyle||'', //3
27405                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
27406                f.itemCls||this.itemCls||''  //5
27407        ], true).getPrevSibling());
27408     },
27409
27410     // private
27411     renderComponent : function(c){
27412         c.render(c.isLayout ? this.el : this.el.createChild());    
27413     },
27414     /**
27415      * Adds a object form elements (using the xtype property as the factory method.)
27416      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
27417      * @param {Object} config 
27418      */
27419     addxtype : function(o)
27420     {
27421         // create the lement.
27422         o.form = this.form;
27423         var fe = Roo.factory(o, Roo.form);
27424         this.form.allItems.push(fe);
27425         this.stack.push(fe);
27426         
27427         if (fe.isFormField) {
27428             this.form.items.add(fe);
27429         }
27430          
27431         return fe;
27432     }
27433 });
27434
27435 /**
27436  * @class Roo.form.Column
27437  * @extends Roo.form.Layout
27438  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
27439  * @constructor
27440  * @param {Object} config Configuration options
27441  */
27442 Roo.form.Column = function(config){
27443     Roo.form.Column.superclass.constructor.call(this, config);
27444 };
27445
27446 Roo.extend(Roo.form.Column, Roo.form.Layout, {
27447     /**
27448      * @cfg {Number/String} width
27449      * The fixed width of the column in pixels or CSS value (defaults to "auto")
27450      */
27451     /**
27452      * @cfg {String/Object} autoCreate
27453      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
27454      */
27455
27456     // private
27457     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
27458
27459     // private
27460     onRender : function(ct, position){
27461         Roo.form.Column.superclass.onRender.call(this, ct, position);
27462         if(this.width){
27463             this.el.setWidth(this.width);
27464         }
27465     }
27466 });
27467
27468
27469 /**
27470  * @class Roo.form.Row
27471  * @extends Roo.form.Layout
27472  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
27473  * @constructor
27474  * @param {Object} config Configuration options
27475  */
27476
27477  
27478 Roo.form.Row = function(config){
27479     Roo.form.Row.superclass.constructor.call(this, config);
27480 };
27481  
27482 Roo.extend(Roo.form.Row, Roo.form.Layout, {
27483       /**
27484      * @cfg {Number/String} width
27485      * The fixed width of the column in pixels or CSS value (defaults to "auto")
27486      */
27487     /**
27488      * @cfg {Number/String} height
27489      * The fixed height of the column in pixels or CSS value (defaults to "auto")
27490      */
27491     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
27492     
27493     padWidth : 20,
27494     // private
27495     onRender : function(ct, position){
27496         //console.log('row render');
27497         if(!this.rowTpl){
27498             var t = new Roo.Template(
27499                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
27500                     '<label for="{0}" style="{2}">{1}{4}</label>',
27501                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
27502                     '</div>',
27503                 '</div>'
27504             );
27505             t.disableFormats = true;
27506             t.compile();
27507             Roo.form.Layout.prototype.rowTpl = t;
27508         }
27509         this.fieldTpl = this.rowTpl;
27510         
27511         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
27512         var labelWidth = 100;
27513         
27514         if ((this.labelAlign != 'top')) {
27515             if (typeof this.labelWidth == 'number') {
27516                 labelWidth = this.labelWidth
27517             }
27518             this.padWidth =  20 + labelWidth;
27519             
27520         }
27521         
27522         Roo.form.Column.superclass.onRender.call(this, ct, position);
27523         if(this.width){
27524             this.el.setWidth(this.width);
27525         }
27526         if(this.height){
27527             this.el.setHeight(this.height);
27528         }
27529     },
27530     
27531     // private
27532     renderField : function(f){
27533         f.fieldEl = this.fieldTpl.append(this.el, [
27534                f.id, f.fieldLabel,
27535                f.labelStyle||this.labelStyle||'',
27536                this.elementStyle||'',
27537                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
27538                f.itemCls||this.itemCls||'',
27539                f.width ? f.width + this.padWidth : 160 + this.padWidth
27540        ],true);
27541     }
27542 });
27543  
27544
27545 /**
27546  * @class Roo.form.FieldSet
27547  * @extends Roo.form.Layout
27548  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
27549  * @constructor
27550  * @param {Object} config Configuration options
27551  */
27552 Roo.form.FieldSet = function(config){
27553     Roo.form.FieldSet.superclass.constructor.call(this, config);
27554 };
27555
27556 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
27557     /**
27558      * @cfg {String} legend
27559      * The text to display as the legend for the FieldSet (defaults to '')
27560      */
27561     /**
27562      * @cfg {String/Object} autoCreate
27563      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
27564      */
27565
27566     // private
27567     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
27568
27569     // private
27570     onRender : function(ct, position){
27571         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
27572         if(this.legend){
27573             this.setLegend(this.legend);
27574         }
27575     },
27576
27577     // private
27578     setLegend : function(text){
27579         if(this.rendered){
27580             this.el.child('legend').update(text);
27581         }
27582     }
27583 });/*
27584  * Based on:
27585  * Ext JS Library 1.1.1
27586  * Copyright(c) 2006-2007, Ext JS, LLC.
27587  *
27588  * Originally Released Under LGPL - original licence link has changed is not relivant.
27589  *
27590  * Fork - LGPL
27591  * <script type="text/javascript">
27592  */
27593 /**
27594  * @class Roo.form.VTypes
27595  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
27596  * @singleton
27597  */
27598 Roo.form.VTypes = function(){
27599     // closure these in so they are only created once.
27600     var alpha = /^[a-zA-Z_]+$/;
27601     var alphanum = /^[a-zA-Z0-9_]+$/;
27602     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
27603     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
27604
27605     // All these messages and functions are configurable
27606     return {
27607         /**
27608          * The function used to validate email addresses
27609          * @param {String} value The email address
27610          */
27611         'email' : function(v){
27612             return email.test(v);
27613         },
27614         /**
27615          * The error text to display when the email validation function returns false
27616          * @type String
27617          */
27618         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
27619         /**
27620          * The keystroke filter mask to be applied on email input
27621          * @type RegExp
27622          */
27623         'emailMask' : /[a-z0-9_\.\-@]/i,
27624
27625         /**
27626          * The function used to validate URLs
27627          * @param {String} value The URL
27628          */
27629         'url' : function(v){
27630             return url.test(v);
27631         },
27632         /**
27633          * The error text to display when the url validation function returns false
27634          * @type String
27635          */
27636         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
27637         
27638         /**
27639          * The function used to validate alpha values
27640          * @param {String} value The value
27641          */
27642         'alpha' : function(v){
27643             return alpha.test(v);
27644         },
27645         /**
27646          * The error text to display when the alpha validation function returns false
27647          * @type String
27648          */
27649         'alphaText' : 'This field should only contain letters and _',
27650         /**
27651          * The keystroke filter mask to be applied on alpha input
27652          * @type RegExp
27653          */
27654         'alphaMask' : /[a-z_]/i,
27655
27656         /**
27657          * The function used to validate alphanumeric values
27658          * @param {String} value The value
27659          */
27660         'alphanum' : function(v){
27661             return alphanum.test(v);
27662         },
27663         /**
27664          * The error text to display when the alphanumeric validation function returns false
27665          * @type String
27666          */
27667         'alphanumText' : 'This field should only contain letters, numbers and _',
27668         /**
27669          * The keystroke filter mask to be applied on alphanumeric input
27670          * @type RegExp
27671          */
27672         'alphanumMask' : /[a-z0-9_]/i
27673     };
27674 }();//<script type="text/javascript">
27675
27676 /**
27677  * @class Roo.form.FCKeditor
27678  * @extends Roo.form.TextArea
27679  * Wrapper around the FCKEditor http://www.fckeditor.net
27680  * @constructor
27681  * Creates a new FCKeditor
27682  * @param {Object} config Configuration options
27683  */
27684 Roo.form.FCKeditor = function(config){
27685     Roo.form.FCKeditor.superclass.constructor.call(this, config);
27686     this.addEvents({
27687          /**
27688          * @event editorinit
27689          * Fired when the editor is initialized - you can add extra handlers here..
27690          * @param {FCKeditor} this
27691          * @param {Object} the FCK object.
27692          */
27693         editorinit : true
27694     });
27695     
27696     
27697 };
27698 Roo.form.FCKeditor.editors = { };
27699 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
27700 {
27701     //defaultAutoCreate : {
27702     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
27703     //},
27704     // private
27705     /**
27706      * @cfg {Object} fck options - see fck manual for details.
27707      */
27708     fckconfig : false,
27709     
27710     /**
27711      * @cfg {Object} fck toolbar set (Basic or Default)
27712      */
27713     toolbarSet : 'Basic',
27714     /**
27715      * @cfg {Object} fck BasePath
27716      */ 
27717     basePath : '/fckeditor/',
27718     
27719     
27720     frame : false,
27721     
27722     value : '',
27723     
27724    
27725     onRender : function(ct, position)
27726     {
27727         if(!this.el){
27728             this.defaultAutoCreate = {
27729                 tag: "textarea",
27730                 style:"width:300px;height:60px;",
27731                 autocomplete: "off"
27732             };
27733         }
27734         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
27735         /*
27736         if(this.grow){
27737             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
27738             if(this.preventScrollbars){
27739                 this.el.setStyle("overflow", "hidden");
27740             }
27741             this.el.setHeight(this.growMin);
27742         }
27743         */
27744         //console.log('onrender' + this.getId() );
27745         Roo.form.FCKeditor.editors[this.getId()] = this;
27746          
27747
27748         this.replaceTextarea() ;
27749         
27750     },
27751     
27752     getEditor : function() {
27753         return this.fckEditor;
27754     },
27755     /**
27756      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
27757      * @param {Mixed} value The value to set
27758      */
27759     
27760     
27761     setValue : function(value)
27762     {
27763         //console.log('setValue: ' + value);
27764         
27765         if(typeof(value) == 'undefined') { // not sure why this is happending...
27766             return;
27767         }
27768         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
27769         
27770         //if(!this.el || !this.getEditor()) {
27771         //    this.value = value;
27772             //this.setValue.defer(100,this,[value]);    
27773         //    return;
27774         //} 
27775         
27776         if(!this.getEditor()) {
27777             return;
27778         }
27779         
27780         this.getEditor().SetData(value);
27781         
27782         //
27783
27784     },
27785
27786     /**
27787      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
27788      * @return {Mixed} value The field value
27789      */
27790     getValue : function()
27791     {
27792         
27793         if (this.frame && this.frame.dom.style.display == 'none') {
27794             return Roo.form.FCKeditor.superclass.getValue.call(this);
27795         }
27796         
27797         if(!this.el || !this.getEditor()) {
27798            
27799            // this.getValue.defer(100,this); 
27800             return this.value;
27801         }
27802        
27803         
27804         var value=this.getEditor().GetData();
27805         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
27806         return Roo.form.FCKeditor.superclass.getValue.call(this);
27807         
27808
27809     },
27810
27811     /**
27812      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
27813      * @return {Mixed} value The field value
27814      */
27815     getRawValue : function()
27816     {
27817         if (this.frame && this.frame.dom.style.display == 'none') {
27818             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
27819         }
27820         
27821         if(!this.el || !this.getEditor()) {
27822             //this.getRawValue.defer(100,this); 
27823             return this.value;
27824             return;
27825         }
27826         
27827         
27828         
27829         var value=this.getEditor().GetData();
27830         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
27831         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
27832          
27833     },
27834     
27835     setSize : function(w,h) {
27836         
27837         
27838         
27839         //if (this.frame && this.frame.dom.style.display == 'none') {
27840         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27841         //    return;
27842         //}
27843         //if(!this.el || !this.getEditor()) {
27844         //    this.setSize.defer(100,this, [w,h]); 
27845         //    return;
27846         //}
27847         
27848         
27849         
27850         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27851         
27852         this.frame.dom.setAttribute('width', w);
27853         this.frame.dom.setAttribute('height', h);
27854         this.frame.setSize(w,h);
27855         
27856     },
27857     
27858     toggleSourceEdit : function(value) {
27859         
27860       
27861          
27862         this.el.dom.style.display = value ? '' : 'none';
27863         this.frame.dom.style.display = value ?  'none' : '';
27864         
27865     },
27866     
27867     
27868     focus: function(tag)
27869     {
27870         if (this.frame.dom.style.display == 'none') {
27871             return Roo.form.FCKeditor.superclass.focus.call(this);
27872         }
27873         if(!this.el || !this.getEditor()) {
27874             this.focus.defer(100,this, [tag]); 
27875             return;
27876         }
27877         
27878         
27879         
27880         
27881         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
27882         this.getEditor().Focus();
27883         if (tgs.length) {
27884             if (!this.getEditor().Selection.GetSelection()) {
27885                 this.focus.defer(100,this, [tag]); 
27886                 return;
27887             }
27888             
27889             
27890             var r = this.getEditor().EditorDocument.createRange();
27891             r.setStart(tgs[0],0);
27892             r.setEnd(tgs[0],0);
27893             this.getEditor().Selection.GetSelection().removeAllRanges();
27894             this.getEditor().Selection.GetSelection().addRange(r);
27895             this.getEditor().Focus();
27896         }
27897         
27898     },
27899     
27900     
27901     
27902     replaceTextarea : function()
27903     {
27904         if ( document.getElementById( this.getId() + '___Frame' ) )
27905             return ;
27906         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
27907         //{
27908             // We must check the elements firstly using the Id and then the name.
27909         var oTextarea = document.getElementById( this.getId() );
27910         
27911         var colElementsByName = document.getElementsByName( this.getId() ) ;
27912          
27913         oTextarea.style.display = 'none' ;
27914
27915         if ( oTextarea.tabIndex ) {            
27916             this.TabIndex = oTextarea.tabIndex ;
27917         }
27918         
27919         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
27920         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
27921         this.frame = Roo.get(this.getId() + '___Frame')
27922     },
27923     
27924     _getConfigHtml : function()
27925     {
27926         var sConfig = '' ;
27927
27928         for ( var o in this.fckconfig ) {
27929             sConfig += sConfig.length > 0  ? '&amp;' : '';
27930             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
27931         }
27932
27933         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
27934     },
27935     
27936     
27937     _getIFrameHtml : function()
27938     {
27939         var sFile = 'fckeditor.html' ;
27940         /* no idea what this is about..
27941         try
27942         {
27943             if ( (/fcksource=true/i).test( window.top.location.search ) )
27944                 sFile = 'fckeditor.original.html' ;
27945         }
27946         catch (e) { 
27947         */
27948
27949         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
27950         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
27951         
27952         
27953         var html = '<iframe id="' + this.getId() +
27954             '___Frame" src="' + sLink +
27955             '" width="' + this.width +
27956             '" height="' + this.height + '"' +
27957             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
27958             ' frameborder="0" scrolling="no"></iframe>' ;
27959
27960         return html ;
27961     },
27962     
27963     _insertHtmlBefore : function( html, element )
27964     {
27965         if ( element.insertAdjacentHTML )       {
27966             // IE
27967             element.insertAdjacentHTML( 'beforeBegin', html ) ;
27968         } else { // Gecko
27969             var oRange = document.createRange() ;
27970             oRange.setStartBefore( element ) ;
27971             var oFragment = oRange.createContextualFragment( html );
27972             element.parentNode.insertBefore( oFragment, element ) ;
27973         }
27974     }
27975     
27976     
27977   
27978     
27979     
27980     
27981     
27982
27983 });
27984
27985 //Roo.reg('fckeditor', Roo.form.FCKeditor);
27986
27987 function FCKeditor_OnComplete(editorInstance){
27988     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
27989     f.fckEditor = editorInstance;
27990     //console.log("loaded");
27991     f.fireEvent('editorinit', f, editorInstance);
27992
27993   
27994
27995  
27996
27997
27998
27999
28000
28001
28002
28003
28004
28005
28006
28007
28008
28009
28010
28011 //<script type="text/javascript">
28012 /**
28013  * @class Roo.form.GridField
28014  * @extends Roo.form.Field
28015  * Embed a grid (or editable grid into a form)
28016  * STATUS ALPHA
28017  * 
28018  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
28019  * it needs 
28020  * xgrid.store = Roo.data.Store
28021  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
28022  * xgrid.store.reader = Roo.data.JsonReader 
28023  * 
28024  * 
28025  * @constructor
28026  * Creates a new GridField
28027  * @param {Object} config Configuration options
28028  */
28029 Roo.form.GridField = function(config){
28030     Roo.form.GridField.superclass.constructor.call(this, config);
28031      
28032 };
28033
28034 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
28035     /**
28036      * @cfg {Number} width  - used to restrict width of grid..
28037      */
28038     width : 100,
28039     /**
28040      * @cfg {Number} height - used to restrict height of grid..
28041      */
28042     height : 50,
28043      /**
28044      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
28045          * 
28046          *}
28047      */
28048     xgrid : false, 
28049     /**
28050      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28051      * {tag: "input", type: "checkbox", autocomplete: "off"})
28052      */
28053    // defaultAutoCreate : { tag: 'div' },
28054     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
28055     /**
28056      * @cfg {String} addTitle Text to include for adding a title.
28057      */
28058     addTitle : false,
28059     //
28060     onResize : function(){
28061         Roo.form.Field.superclass.onResize.apply(this, arguments);
28062     },
28063
28064     initEvents : function(){
28065         // Roo.form.Checkbox.superclass.initEvents.call(this);
28066         // has no events...
28067        
28068     },
28069
28070
28071     getResizeEl : function(){
28072         return this.wrap;
28073     },
28074
28075     getPositionEl : function(){
28076         return this.wrap;
28077     },
28078
28079     // private
28080     onRender : function(ct, position){
28081         
28082         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
28083         var style = this.style;
28084         delete this.style;
28085         
28086         Roo.form.GridField.superclass.onRender.call(this, ct, position);
28087         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
28088         this.viewEl = this.wrap.createChild({ tag: 'div' });
28089         if (style) {
28090             this.viewEl.applyStyles(style);
28091         }
28092         if (this.width) {
28093             this.viewEl.setWidth(this.width);
28094         }
28095         if (this.height) {
28096             this.viewEl.setHeight(this.height);
28097         }
28098         //if(this.inputValue !== undefined){
28099         //this.setValue(this.value);
28100         
28101         
28102         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
28103         
28104         
28105         this.grid.render();
28106         this.grid.getDataSource().on('remove', this.refreshValue, this);
28107         this.grid.getDataSource().on('update', this.refreshValue, this);
28108         this.grid.on('afteredit', this.refreshValue, this);
28109  
28110     },
28111      
28112     
28113     /**
28114      * Sets the value of the item. 
28115      * @param {String} either an object  or a string..
28116      */
28117     setValue : function(v){
28118         //this.value = v;
28119         v = v || []; // empty set..
28120         // this does not seem smart - it really only affects memoryproxy grids..
28121         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
28122             var ds = this.grid.getDataSource();
28123             // assumes a json reader..
28124             var data = {}
28125             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
28126             ds.loadData( data);
28127         }
28128         Roo.form.GridField.superclass.setValue.call(this, v);
28129         this.refreshValue();
28130         // should load data in the grid really....
28131     },
28132     
28133     // private
28134     refreshValue: function() {
28135          var val = [];
28136         this.grid.getDataSource().each(function(r) {
28137             val.push(r.data);
28138         });
28139         this.el.dom.value = Roo.encode(val);
28140     }
28141     
28142      
28143     
28144     
28145 });/*
28146  * Based on:
28147  * Ext JS Library 1.1.1
28148  * Copyright(c) 2006-2007, Ext JS, LLC.
28149  *
28150  * Originally Released Under LGPL - original licence link has changed is not relivant.
28151  *
28152  * Fork - LGPL
28153  * <script type="text/javascript">
28154  */
28155 /**
28156  * @class Roo.form.DisplayField
28157  * @extends Roo.form.Field
28158  * A generic Field to display non-editable data.
28159  * @constructor
28160  * Creates a new Display Field item.
28161  * @param {Object} config Configuration options
28162  */
28163 Roo.form.DisplayField = function(config){
28164     Roo.form.DisplayField.superclass.constructor.call(this, config);
28165     
28166 };
28167
28168 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
28169     inputType:      'hidden',
28170     allowBlank:     true,
28171     readOnly:         true,
28172     
28173  
28174     /**
28175      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
28176      */
28177     focusClass : undefined,
28178     /**
28179      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
28180      */
28181     fieldClass: 'x-form-field',
28182     
28183      /**
28184      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
28185      */
28186     valueRenderer: undefined,
28187     
28188     width: 100,
28189     /**
28190      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28191      * {tag: "input", type: "checkbox", autocomplete: "off"})
28192      */
28193      
28194  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
28195
28196     onResize : function(){
28197         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
28198         
28199     },
28200
28201     initEvents : function(){
28202         // Roo.form.Checkbox.superclass.initEvents.call(this);
28203         // has no events...
28204        
28205     },
28206
28207
28208     getResizeEl : function(){
28209         return this.wrap;
28210     },
28211
28212     getPositionEl : function(){
28213         return this.wrap;
28214     },
28215
28216     // private
28217     onRender : function(ct, position){
28218         
28219         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
28220         //if(this.inputValue !== undefined){
28221         this.wrap = this.el.wrap();
28222         
28223         this.viewEl = this.wrap.createChild({ tag: 'div'});
28224         
28225         if (this.bodyStyle) {
28226             this.viewEl.applyStyles(this.bodyStyle);
28227         }
28228         //this.viewEl.setStyle('padding', '2px');
28229         
28230         this.setValue(this.value);
28231         
28232     },
28233 /*
28234     // private
28235     initValue : Roo.emptyFn,
28236
28237   */
28238
28239         // private
28240     onClick : function(){
28241         
28242     },
28243
28244     /**
28245      * Sets the checked state of the checkbox.
28246      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
28247      */
28248     setValue : function(v){
28249         this.value = v;
28250         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
28251         // this might be called before we have a dom element..
28252         if (!this.viewEl) {
28253             return;
28254         }
28255         this.viewEl.dom.innerHTML = html;
28256         Roo.form.DisplayField.superclass.setValue.call(this, v);
28257
28258     }
28259 });//<script type="text/javasscript">
28260  
28261
28262 /**
28263  * @class Roo.DDView
28264  * A DnD enabled version of Roo.View.
28265  * @param {Element/String} container The Element in which to create the View.
28266  * @param {String} tpl The template string used to create the markup for each element of the View
28267  * @param {Object} config The configuration properties. These include all the config options of
28268  * {@link Roo.View} plus some specific to this class.<br>
28269  * <p>
28270  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28271  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28272  * <p>
28273  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28274 .x-view-drag-insert-above {
28275         border-top:1px dotted #3366cc;
28276 }
28277 .x-view-drag-insert-below {
28278         border-bottom:1px dotted #3366cc;
28279 }
28280 </code></pre>
28281  * 
28282  */
28283  
28284 Roo.DDView = function(container, tpl, config) {
28285     Roo.DDView.superclass.constructor.apply(this, arguments);
28286     this.getEl().setStyle("outline", "0px none");
28287     this.getEl().unselectable();
28288     if (this.dragGroup) {
28289                 this.setDraggable(this.dragGroup.split(","));
28290     }
28291     if (this.dropGroup) {
28292                 this.setDroppable(this.dropGroup.split(","));
28293     }
28294     if (this.deletable) {
28295         this.setDeletable();
28296     }
28297     this.isDirtyFlag = false;
28298         this.addEvents({
28299                 "drop" : true
28300         });
28301 };
28302
28303 Roo.extend(Roo.DDView, Roo.View, {
28304 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28305 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28306 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28307 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28308
28309         isFormField: true,
28310
28311         reset: Roo.emptyFn,
28312         
28313         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28314
28315         validate: function() {
28316                 return true;
28317         },
28318         
28319         destroy: function() {
28320                 this.purgeListeners();
28321                 this.getEl.removeAllListeners();
28322                 this.getEl().remove();
28323                 if (this.dragZone) {
28324                         if (this.dragZone.destroy) {
28325                                 this.dragZone.destroy();
28326                         }
28327                 }
28328                 if (this.dropZone) {
28329                         if (this.dropZone.destroy) {
28330                                 this.dropZone.destroy();
28331                         }
28332                 }
28333         },
28334
28335 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28336         getName: function() {
28337                 return this.name;
28338         },
28339
28340 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28341         setValue: function(v) {
28342                 if (!this.store) {
28343                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28344                 }
28345                 var data = {};
28346                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28347                 this.store.proxy = new Roo.data.MemoryProxy(data);
28348                 this.store.load();
28349         },
28350
28351 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28352         getValue: function() {
28353                 var result = '(';
28354                 this.store.each(function(rec) {
28355                         result += rec.id + ',';
28356                 });
28357                 return result.substr(0, result.length - 1) + ')';
28358         },
28359         
28360         getIds: function() {
28361                 var i = 0, result = new Array(this.store.getCount());
28362                 this.store.each(function(rec) {
28363                         result[i++] = rec.id;
28364                 });
28365                 return result;
28366         },
28367         
28368         isDirty: function() {
28369                 return this.isDirtyFlag;
28370         },
28371
28372 /**
28373  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28374  *      whole Element becomes the target, and this causes the drop gesture to append.
28375  */
28376     getTargetFromEvent : function(e) {
28377                 var target = e.getTarget();
28378                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28379                 target = target.parentNode;
28380                 }
28381                 if (!target) {
28382                         target = this.el.dom.lastChild || this.el.dom;
28383                 }
28384                 return target;
28385     },
28386
28387 /**
28388  *      Create the drag data which consists of an object which has the property "ddel" as
28389  *      the drag proxy element. 
28390  */
28391     getDragData : function(e) {
28392         var target = this.findItemFromChild(e.getTarget());
28393                 if(target) {
28394                         this.handleSelection(e);
28395                         var selNodes = this.getSelectedNodes();
28396             var dragData = {
28397                 source: this,
28398                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28399                 nodes: selNodes,
28400                 records: []
28401                         };
28402                         var selectedIndices = this.getSelectedIndexes();
28403                         for (var i = 0; i < selectedIndices.length; i++) {
28404                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28405                         }
28406                         if (selNodes.length == 1) {
28407                                 dragData.ddel = target.cloneNode(true); // the div element
28408                         } else {
28409                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28410                                 div.className = 'multi-proxy';
28411                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28412                                         div.appendChild(selNodes[i].cloneNode(true));
28413                                 }
28414                                 dragData.ddel = div;
28415                         }
28416             //console.log(dragData)
28417             //console.log(dragData.ddel.innerHTML)
28418                         return dragData;
28419                 }
28420         //console.log('nodragData')
28421                 return false;
28422     },
28423     
28424 /**     Specify to which ddGroup items in this DDView may be dragged. */
28425     setDraggable: function(ddGroup) {
28426         if (ddGroup instanceof Array) {
28427                 Roo.each(ddGroup, this.setDraggable, this);
28428                 return;
28429         }
28430         if (this.dragZone) {
28431                 this.dragZone.addToGroup(ddGroup);
28432         } else {
28433                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28434                                 containerScroll: true,
28435                                 ddGroup: ddGroup 
28436
28437                         });
28438 //                      Draggability implies selection. DragZone's mousedown selects the element.
28439                         if (!this.multiSelect) { this.singleSelect = true; }
28440
28441 //                      Wire the DragZone's handlers up to methods in *this*
28442                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28443                 }
28444     },
28445
28446 /**     Specify from which ddGroup this DDView accepts drops. */
28447     setDroppable: function(ddGroup) {
28448         if (ddGroup instanceof Array) {
28449                 Roo.each(ddGroup, this.setDroppable, this);
28450                 return;
28451         }
28452         if (this.dropZone) {
28453                 this.dropZone.addToGroup(ddGroup);
28454         } else {
28455                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28456                                 containerScroll: true,
28457                                 ddGroup: ddGroup
28458                         });
28459
28460 //                      Wire the DropZone's handlers up to methods in *this*
28461                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28462                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28463                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28464                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28465                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28466                 }
28467     },
28468
28469 /**     Decide whether to drop above or below a View node. */
28470     getDropPoint : function(e, n, dd){
28471         if (n == this.el.dom) { return "above"; }
28472                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28473                 var c = t + (b - t) / 2;
28474                 var y = Roo.lib.Event.getPageY(e);
28475                 if(y <= c) {
28476                         return "above";
28477                 }else{
28478                         return "below";
28479                 }
28480     },
28481
28482     onNodeEnter : function(n, dd, e, data){
28483                 return false;
28484     },
28485     
28486     onNodeOver : function(n, dd, e, data){
28487                 var pt = this.getDropPoint(e, n, dd);
28488                 // set the insert point style on the target node
28489                 var dragElClass = this.dropNotAllowed;
28490                 if (pt) {
28491                         var targetElClass;
28492                         if (pt == "above"){
28493                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28494                                 targetElClass = "x-view-drag-insert-above";
28495                         } else {
28496                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28497                                 targetElClass = "x-view-drag-insert-below";
28498                         }
28499                         if (this.lastInsertClass != targetElClass){
28500                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28501                                 this.lastInsertClass = targetElClass;
28502                         }
28503                 }
28504                 return dragElClass;
28505         },
28506
28507     onNodeOut : function(n, dd, e, data){
28508                 this.removeDropIndicators(n);
28509     },
28510
28511     onNodeDrop : function(n, dd, e, data){
28512         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28513                 return false;
28514         }
28515         var pt = this.getDropPoint(e, n, dd);
28516                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28517                 if (pt == "below") { insertAt++; }
28518                 for (var i = 0; i < data.records.length; i++) {
28519                         var r = data.records[i];
28520                         var dup = this.store.getById(r.id);
28521                         if (dup && (dd != this.dragZone)) {
28522                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28523                         } else {
28524                                 if (data.copy) {
28525                                         this.store.insert(insertAt++, r.copy());
28526                                 } else {
28527                                         data.source.isDirtyFlag = true;
28528                                         r.store.remove(r);
28529                                         this.store.insert(insertAt++, r);
28530                                 }
28531                                 this.isDirtyFlag = true;
28532                         }
28533                 }
28534                 this.dragZone.cachedTarget = null;
28535                 return true;
28536     },
28537
28538     removeDropIndicators : function(n){
28539                 if(n){
28540                         Roo.fly(n).removeClass([
28541                                 "x-view-drag-insert-above",
28542                                 "x-view-drag-insert-below"]);
28543                         this.lastInsertClass = "_noclass";
28544                 }
28545     },
28546
28547 /**
28548  *      Utility method. Add a delete option to the DDView's context menu.
28549  *      @param {String} imageUrl The URL of the "delete" icon image.
28550  */
28551         setDeletable: function(imageUrl) {
28552                 if (!this.singleSelect && !this.multiSelect) {
28553                         this.singleSelect = true;
28554                 }
28555                 var c = this.getContextMenu();
28556                 this.contextMenu.on("itemclick", function(item) {
28557                         switch (item.id) {
28558                                 case "delete":
28559                                         this.remove(this.getSelectedIndexes());
28560                                         break;
28561                         }
28562                 }, this);
28563                 this.contextMenu.add({
28564                         icon: imageUrl,
28565                         id: "delete",
28566                         text: 'Delete'
28567                 });
28568         },
28569         
28570 /**     Return the context menu for this DDView. */
28571         getContextMenu: function() {
28572                 if (!this.contextMenu) {
28573 //                      Create the View's context menu
28574                         this.contextMenu = new Roo.menu.Menu({
28575                                 id: this.id + "-contextmenu"
28576                         });
28577                         this.el.on("contextmenu", this.showContextMenu, this);
28578                 }
28579                 return this.contextMenu;
28580         },
28581         
28582         disableContextMenu: function() {
28583                 if (this.contextMenu) {
28584                         this.el.un("contextmenu", this.showContextMenu, this);
28585                 }
28586         },
28587
28588         showContextMenu: function(e, item) {
28589         item = this.findItemFromChild(e.getTarget());
28590                 if (item) {
28591                         e.stopEvent();
28592                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28593                         this.contextMenu.showAt(e.getXY());
28594             }
28595     },
28596
28597 /**
28598  *      Remove {@link Roo.data.Record}s at the specified indices.
28599  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28600  */
28601     remove: function(selectedIndices) {
28602                 selectedIndices = [].concat(selectedIndices);
28603                 for (var i = 0; i < selectedIndices.length; i++) {
28604                         var rec = this.store.getAt(selectedIndices[i]);
28605                         this.store.remove(rec);
28606                 }
28607     },
28608
28609 /**
28610  *      Double click fires the event, but also, if this is draggable, and there is only one other
28611  *      related DropZone, it transfers the selected node.
28612  */
28613     onDblClick : function(e){
28614         var item = this.findItemFromChild(e.getTarget());
28615         if(item){
28616             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28617                 return false;
28618             }
28619             if (this.dragGroup) {
28620                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28621                     while (targets.indexOf(this.dropZone) > -1) {
28622                             targets.remove(this.dropZone);
28623                                 }
28624                     if (targets.length == 1) {
28625                                         this.dragZone.cachedTarget = null;
28626                         var el = Roo.get(targets[0].getEl());
28627                         var box = el.getBox(true);
28628                         targets[0].onNodeDrop(el.dom, {
28629                                 target: el.dom,
28630                                 xy: [box.x, box.y + box.height - 1]
28631                         }, null, this.getDragData(e));
28632                     }
28633                 }
28634         }
28635     },
28636     
28637     handleSelection: function(e) {
28638                 this.dragZone.cachedTarget = null;
28639         var item = this.findItemFromChild(e.getTarget());
28640         if (!item) {
28641                 this.clearSelections(true);
28642                 return;
28643         }
28644                 if (item && (this.multiSelect || this.singleSelect)){
28645                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28646                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28647                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28648                                 this.unselect(item);
28649                         } else {
28650                                 this.select(item, this.multiSelect && e.ctrlKey);
28651                                 this.lastSelection = item;
28652                         }
28653                 }
28654     },
28655
28656     onItemClick : function(item, index, e){
28657                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28658                         return false;
28659                 }
28660                 return true;
28661     },
28662
28663     unselect : function(nodeInfo, suppressEvent){
28664                 var node = this.getNode(nodeInfo);
28665                 if(node && this.isSelected(node)){
28666                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28667                                 Roo.fly(node).removeClass(this.selectedClass);
28668                                 this.selections.remove(node);
28669                                 if(!suppressEvent){
28670                                         this.fireEvent("selectionchange", this, this.selections);
28671                                 }
28672                         }
28673                 }
28674     }
28675 });
28676 /*
28677  * Based on:
28678  * Ext JS Library 1.1.1
28679  * Copyright(c) 2006-2007, Ext JS, LLC.
28680  *
28681  * Originally Released Under LGPL - original licence link has changed is not relivant.
28682  *
28683  * Fork - LGPL
28684  * <script type="text/javascript">
28685  */
28686  
28687 /**
28688  * @class Roo.LayoutManager
28689  * @extends Roo.util.Observable
28690  * Base class for layout managers.
28691  */
28692 Roo.LayoutManager = function(container, config){
28693     Roo.LayoutManager.superclass.constructor.call(this);
28694     this.el = Roo.get(container);
28695     // ie scrollbar fix
28696     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28697         document.body.scroll = "no";
28698     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28699         this.el.position('relative');
28700     }
28701     this.id = this.el.id;
28702     this.el.addClass("x-layout-container");
28703     /** false to disable window resize monitoring @type Boolean */
28704     this.monitorWindowResize = true;
28705     this.regions = {};
28706     this.addEvents({
28707         /**
28708          * @event layout
28709          * Fires when a layout is performed. 
28710          * @param {Roo.LayoutManager} this
28711          */
28712         "layout" : true,
28713         /**
28714          * @event regionresized
28715          * Fires when the user resizes a region. 
28716          * @param {Roo.LayoutRegion} region The resized region
28717          * @param {Number} newSize The new size (width for east/west, height for north/south)
28718          */
28719         "regionresized" : true,
28720         /**
28721          * @event regioncollapsed
28722          * Fires when a region is collapsed. 
28723          * @param {Roo.LayoutRegion} region The collapsed region
28724          */
28725         "regioncollapsed" : true,
28726         /**
28727          * @event regionexpanded
28728          * Fires when a region is expanded.  
28729          * @param {Roo.LayoutRegion} region The expanded region
28730          */
28731         "regionexpanded" : true
28732     });
28733     this.updating = false;
28734     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28735 };
28736
28737 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28738     /**
28739      * Returns true if this layout is currently being updated
28740      * @return {Boolean}
28741      */
28742     isUpdating : function(){
28743         return this.updating; 
28744     },
28745     
28746     /**
28747      * Suspend the LayoutManager from doing auto-layouts while
28748      * making multiple add or remove calls
28749      */
28750     beginUpdate : function(){
28751         this.updating = true;    
28752     },
28753     
28754     /**
28755      * Restore auto-layouts and optionally disable the manager from performing a layout
28756      * @param {Boolean} noLayout true to disable a layout update 
28757      */
28758     endUpdate : function(noLayout){
28759         this.updating = false;
28760         if(!noLayout){
28761             this.layout();
28762         }    
28763     },
28764     
28765     layout: function(){
28766         
28767     },
28768     
28769     onRegionResized : function(region, newSize){
28770         this.fireEvent("regionresized", region, newSize);
28771         this.layout();
28772     },
28773     
28774     onRegionCollapsed : function(region){
28775         this.fireEvent("regioncollapsed", region);
28776     },
28777     
28778     onRegionExpanded : function(region){
28779         this.fireEvent("regionexpanded", region);
28780     },
28781         
28782     /**
28783      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
28784      * performs box-model adjustments.
28785      * @return {Object} The size as an object {width: (the width), height: (the height)}
28786      */
28787     getViewSize : function(){
28788         var size;
28789         if(this.el.dom != document.body){
28790             size = this.el.getSize();
28791         }else{
28792             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
28793         }
28794         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
28795         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28796         return size;
28797     },
28798     
28799     /**
28800      * Returns the Element this layout is bound to.
28801      * @return {Roo.Element}
28802      */
28803     getEl : function(){
28804         return this.el;
28805     },
28806     
28807     /**
28808      * Returns the specified region.
28809      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
28810      * @return {Roo.LayoutRegion}
28811      */
28812     getRegion : function(target){
28813         return this.regions[target.toLowerCase()];
28814     },
28815     
28816     onWindowResize : function(){
28817         if(this.monitorWindowResize){
28818             this.layout();
28819         }
28820     }
28821 });/*
28822  * Based on:
28823  * Ext JS Library 1.1.1
28824  * Copyright(c) 2006-2007, Ext JS, LLC.
28825  *
28826  * Originally Released Under LGPL - original licence link has changed is not relivant.
28827  *
28828  * Fork - LGPL
28829  * <script type="text/javascript">
28830  */
28831 /**
28832  * @class Roo.BorderLayout
28833  * @extends Roo.LayoutManager
28834  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
28835  * please see: <br><br>
28836  * <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>
28837  * <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>
28838  * Example:
28839  <pre><code>
28840  var layout = new Roo.BorderLayout(document.body, {
28841     north: {
28842         initialSize: 25,
28843         titlebar: false
28844     },
28845     west: {
28846         split:true,
28847         initialSize: 200,
28848         minSize: 175,
28849         maxSize: 400,
28850         titlebar: true,
28851         collapsible: true
28852     },
28853     east: {
28854         split:true,
28855         initialSize: 202,
28856         minSize: 175,
28857         maxSize: 400,
28858         titlebar: true,
28859         collapsible: true
28860     },
28861     south: {
28862         split:true,
28863         initialSize: 100,
28864         minSize: 100,
28865         maxSize: 200,
28866         titlebar: true,
28867         collapsible: true
28868     },
28869     center: {
28870         titlebar: true,
28871         autoScroll:true,
28872         resizeTabs: true,
28873         minTabWidth: 50,
28874         preferredTabWidth: 150
28875     }
28876 });
28877
28878 // shorthand
28879 var CP = Roo.ContentPanel;
28880
28881 layout.beginUpdate();
28882 layout.add("north", new CP("north", "North"));
28883 layout.add("south", new CP("south", {title: "South", closable: true}));
28884 layout.add("west", new CP("west", {title: "West"}));
28885 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
28886 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
28887 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
28888 layout.getRegion("center").showPanel("center1");
28889 layout.endUpdate();
28890 </code></pre>
28891
28892 <b>The container the layout is rendered into can be either the body element or any other element.
28893 If it is not the body element, the container needs to either be an absolute positioned element,
28894 or you will need to add "position:relative" to the css of the container.  You will also need to specify
28895 the container size if it is not the body element.</b>
28896
28897 * @constructor
28898 * Create a new BorderLayout
28899 * @param {String/HTMLElement/Element} container The container this layout is bound to
28900 * @param {Object} config Configuration options
28901  */
28902 Roo.BorderLayout = function(container, config){
28903     config = config || {};
28904     Roo.BorderLayout.superclass.constructor.call(this, container, config);
28905     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
28906     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
28907         var target = this.factory.validRegions[i];
28908         if(config[target]){
28909             this.addRegion(target, config[target]);
28910         }
28911     }
28912 };
28913
28914 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
28915     /**
28916      * Creates and adds a new region if it doesn't already exist.
28917      * @param {String} target The target region key (north, south, east, west or center).
28918      * @param {Object} config The regions config object
28919      * @return {BorderLayoutRegion} The new region
28920      */
28921     addRegion : function(target, config){
28922         if(!this.regions[target]){
28923             var r = this.factory.create(target, this, config);
28924             this.bindRegion(target, r);
28925         }
28926         return this.regions[target];
28927     },
28928
28929     // private (kinda)
28930     bindRegion : function(name, r){
28931         this.regions[name] = r;
28932         r.on("visibilitychange", this.layout, this);
28933         r.on("paneladded", this.layout, this);
28934         r.on("panelremoved", this.layout, this);
28935         r.on("invalidated", this.layout, this);
28936         r.on("resized", this.onRegionResized, this);
28937         r.on("collapsed", this.onRegionCollapsed, this);
28938         r.on("expanded", this.onRegionExpanded, this);
28939     },
28940
28941     /**
28942      * Performs a layout update.
28943      */
28944     layout : function(){
28945         if(this.updating) return;
28946         var size = this.getViewSize();
28947         var w = size.width;
28948         var h = size.height;
28949         var centerW = w;
28950         var centerH = h;
28951         var centerY = 0;
28952         var centerX = 0;
28953         //var x = 0, y = 0;
28954
28955         var rs = this.regions;
28956         var north = rs["north"];
28957         var south = rs["south"]; 
28958         var west = rs["west"];
28959         var east = rs["east"];
28960         var center = rs["center"];
28961         //if(this.hideOnLayout){ // not supported anymore
28962             //c.el.setStyle("display", "none");
28963         //}
28964         if(north && north.isVisible()){
28965             var b = north.getBox();
28966             var m = north.getMargins();
28967             b.width = w - (m.left+m.right);
28968             b.x = m.left;
28969             b.y = m.top;
28970             centerY = b.height + b.y + m.bottom;
28971             centerH -= centerY;
28972             north.updateBox(this.safeBox(b));
28973         }
28974         if(south && south.isVisible()){
28975             var b = south.getBox();
28976             var m = south.getMargins();
28977             b.width = w - (m.left+m.right);
28978             b.x = m.left;
28979             var totalHeight = (b.height + m.top + m.bottom);
28980             b.y = h - totalHeight + m.top;
28981             centerH -= totalHeight;
28982             south.updateBox(this.safeBox(b));
28983         }
28984         if(west && west.isVisible()){
28985             var b = west.getBox();
28986             var m = west.getMargins();
28987             b.height = centerH - (m.top+m.bottom);
28988             b.x = m.left;
28989             b.y = centerY + m.top;
28990             var totalWidth = (b.width + m.left + m.right);
28991             centerX += totalWidth;
28992             centerW -= totalWidth;
28993             west.updateBox(this.safeBox(b));
28994         }
28995         if(east && east.isVisible()){
28996             var b = east.getBox();
28997             var m = east.getMargins();
28998             b.height = centerH - (m.top+m.bottom);
28999             var totalWidth = (b.width + m.left + m.right);
29000             b.x = w - totalWidth + m.left;
29001             b.y = centerY + m.top;
29002             centerW -= totalWidth;
29003             east.updateBox(this.safeBox(b));
29004         }
29005         if(center){
29006             var m = center.getMargins();
29007             var centerBox = {
29008                 x: centerX + m.left,
29009                 y: centerY + m.top,
29010                 width: centerW - (m.left+m.right),
29011                 height: centerH - (m.top+m.bottom)
29012             };
29013             //if(this.hideOnLayout){
29014                 //center.el.setStyle("display", "block");
29015             //}
29016             center.updateBox(this.safeBox(centerBox));
29017         }
29018         this.el.repaint();
29019         this.fireEvent("layout", this);
29020     },
29021
29022     // private
29023     safeBox : function(box){
29024         box.width = Math.max(0, box.width);
29025         box.height = Math.max(0, box.height);
29026         return box;
29027     },
29028
29029     /**
29030      * Adds a ContentPanel (or subclass) to this layout.
29031      * @param {String} target The target region key (north, south, east, west or center).
29032      * @param {Roo.ContentPanel} panel The panel to add
29033      * @return {Roo.ContentPanel} The added panel
29034      */
29035     add : function(target, panel){
29036          
29037         target = target.toLowerCase();
29038         return this.regions[target].add(panel);
29039     },
29040
29041     /**
29042      * Remove a ContentPanel (or subclass) to this layout.
29043      * @param {String} target The target region key (north, south, east, west or center).
29044      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
29045      * @return {Roo.ContentPanel} The removed panel
29046      */
29047     remove : function(target, panel){
29048         target = target.toLowerCase();
29049         return this.regions[target].remove(panel);
29050     },
29051
29052     /**
29053      * Searches all regions for a panel with the specified id
29054      * @param {String} panelId
29055      * @return {Roo.ContentPanel} The panel or null if it wasn't found
29056      */
29057     findPanel : function(panelId){
29058         var rs = this.regions;
29059         for(var target in rs){
29060             if(typeof rs[target] != "function"){
29061                 var p = rs[target].getPanel(panelId);
29062                 if(p){
29063                     return p;
29064                 }
29065             }
29066         }
29067         return null;
29068     },
29069
29070     /**
29071      * Searches all regions for a panel with the specified id and activates (shows) it.
29072      * @param {String/ContentPanel} panelId The panels id or the panel itself
29073      * @return {Roo.ContentPanel} The shown panel or null
29074      */
29075     showPanel : function(panelId) {
29076       var rs = this.regions;
29077       for(var target in rs){
29078          var r = rs[target];
29079          if(typeof r != "function"){
29080             if(r.hasPanel(panelId)){
29081                return r.showPanel(panelId);
29082             }
29083          }
29084       }
29085       return null;
29086    },
29087
29088    /**
29089      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
29090      * @param {Roo.state.Provider} provider (optional) An alternate state provider
29091      */
29092     restoreState : function(provider){
29093         if(!provider){
29094             provider = Roo.state.Manager;
29095         }
29096         var sm = new Roo.LayoutStateManager();
29097         sm.init(this, provider);
29098     },
29099
29100     /**
29101      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
29102      * object should contain properties for each region to add ContentPanels to, and each property's value should be
29103      * a valid ContentPanel config object.  Example:
29104      * <pre><code>
29105 // Create the main layout
29106 var layout = new Roo.BorderLayout('main-ct', {
29107     west: {
29108         split:true,
29109         minSize: 175,
29110         titlebar: true
29111     },
29112     center: {
29113         title:'Components'
29114     }
29115 }, 'main-ct');
29116
29117 // Create and add multiple ContentPanels at once via configs
29118 layout.batchAdd({
29119    west: {
29120        id: 'source-files',
29121        autoCreate:true,
29122        title:'Ext Source Files',
29123        autoScroll:true,
29124        fitToFrame:true
29125    },
29126    center : {
29127        el: cview,
29128        autoScroll:true,
29129        fitToFrame:true,
29130        toolbar: tb,
29131        resizeEl:'cbody'
29132    }
29133 });
29134 </code></pre>
29135      * @param {Object} regions An object containing ContentPanel configs by region name
29136      */
29137     batchAdd : function(regions){
29138         this.beginUpdate();
29139         for(var rname in regions){
29140             var lr = this.regions[rname];
29141             if(lr){
29142                 this.addTypedPanels(lr, regions[rname]);
29143             }
29144         }
29145         this.endUpdate();
29146     },
29147
29148     // private
29149     addTypedPanels : function(lr, ps){
29150         if(typeof ps == 'string'){
29151             lr.add(new Roo.ContentPanel(ps));
29152         }
29153         else if(ps instanceof Array){
29154             for(var i =0, len = ps.length; i < len; i++){
29155                 this.addTypedPanels(lr, ps[i]);
29156             }
29157         }
29158         else if(!ps.events){ // raw config?
29159             var el = ps.el;
29160             delete ps.el; // prevent conflict
29161             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29162         }
29163         else {  // panel object assumed!
29164             lr.add(ps);
29165         }
29166     },
29167     /**
29168      * Adds a xtype elements to the layout.
29169      * <pre><code>
29170
29171 layout.addxtype({
29172        xtype : 'ContentPanel',
29173        region: 'west',
29174        items: [ .... ]
29175    }
29176 );
29177
29178 layout.addxtype({
29179         xtype : 'NestedLayoutPanel',
29180         region: 'west',
29181         layout: {
29182            center: { },
29183            west: { }   
29184         },
29185         items : [ ... list of content panels or nested layout panels.. ]
29186    }
29187 );
29188 </code></pre>
29189      * @param {Object} cfg Xtype definition of item to add.
29190      */
29191     addxtype : function(cfg)
29192     {
29193         // basically accepts a pannel...
29194         // can accept a layout region..!?!?
29195        // console.log('BorderLayout add ' + cfg.xtype)
29196         
29197         if (!cfg.xtype.match(/Panel$/)) {
29198             return false;
29199         }
29200         var ret = false;
29201         var region = cfg.region;
29202         delete cfg.region;
29203         
29204           
29205         var xitems = [];
29206         if (cfg.items) {
29207             xitems = cfg.items;
29208             delete cfg.items;
29209         }
29210         
29211         
29212         switch(cfg.xtype) 
29213         {
29214             case 'ContentPanel':  // ContentPanel (el, cfg)
29215             case 'ScrollPanel':  // ContentPanel (el, cfg)
29216                 if(cfg.autoCreate) {
29217                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29218                 } else {
29219                     var el = this.el.createChild();
29220                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29221                 }
29222                 
29223                 this.add(region, ret);
29224                 break;
29225             
29226             
29227             case 'TreePanel': // our new panel!
29228                 cfg.el = this.el.createChild();
29229                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29230                 this.add(region, ret);
29231                 break;
29232             
29233             case 'NestedLayoutPanel': 
29234                 // create a new Layout (which is  a Border Layout...
29235                 var el = this.el.createChild();
29236                 var clayout = cfg.layout;
29237                 delete cfg.layout;
29238                 clayout.items   = clayout.items  || [];
29239                 // replace this exitems with the clayout ones..
29240                 xitems = clayout.items;
29241                  
29242                 
29243                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29244                     cfg.background = false;
29245                 }
29246                 var layout = new Roo.BorderLayout(el, clayout);
29247                 
29248                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29249                 //console.log('adding nested layout panel '  + cfg.toSource());
29250                 this.add(region, ret);
29251                 
29252                 break;
29253                 
29254             case 'GridPanel': 
29255             
29256                 // needs grid and region
29257                 
29258                 //var el = this.getRegion(region).el.createChild();
29259                 var el = this.el.createChild();
29260                 // create the grid first...
29261                 
29262                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29263                 delete cfg.grid;
29264                 if (region == 'center' && this.active ) {
29265                     cfg.background = false;
29266                 }
29267                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29268                 
29269                 this.add(region, ret);
29270                 if (cfg.background) {
29271                     ret.on('activate', function(gp) {
29272                         if (!gp.grid.rendered) {
29273                             gp.grid.render();
29274                         }
29275                     });
29276                 } else {
29277                     grid.render();
29278                 }
29279                 break;
29280            
29281                
29282                 
29283                 
29284             default: 
29285                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29286                 return;
29287              // GridPanel (grid, cfg)
29288             
29289         }
29290         this.beginUpdate();
29291         // add children..
29292         Roo.each(xitems, function(i)  {
29293             ret.addxtype(i);
29294         });
29295         this.endUpdate();
29296         return ret;
29297         
29298     }
29299 });
29300
29301 /**
29302  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29303  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29304  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29305  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29306  * <pre><code>
29307 // shorthand
29308 var CP = Roo.ContentPanel;
29309
29310 var layout = Roo.BorderLayout.create({
29311     north: {
29312         initialSize: 25,
29313         titlebar: false,
29314         panels: [new CP("north", "North")]
29315     },
29316     west: {
29317         split:true,
29318         initialSize: 200,
29319         minSize: 175,
29320         maxSize: 400,
29321         titlebar: true,
29322         collapsible: true,
29323         panels: [new CP("west", {title: "West"})]
29324     },
29325     east: {
29326         split:true,
29327         initialSize: 202,
29328         minSize: 175,
29329         maxSize: 400,
29330         titlebar: true,
29331         collapsible: true,
29332         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29333     },
29334     south: {
29335         split:true,
29336         initialSize: 100,
29337         minSize: 100,
29338         maxSize: 200,
29339         titlebar: true,
29340         collapsible: true,
29341         panels: [new CP("south", {title: "South", closable: true})]
29342     },
29343     center: {
29344         titlebar: true,
29345         autoScroll:true,
29346         resizeTabs: true,
29347         minTabWidth: 50,
29348         preferredTabWidth: 150,
29349         panels: [
29350             new CP("center1", {title: "Close Me", closable: true}),
29351             new CP("center2", {title: "Center Panel", closable: false})
29352         ]
29353     }
29354 }, document.body);
29355
29356 layout.getRegion("center").showPanel("center1");
29357 </code></pre>
29358  * @param config
29359  * @param targetEl
29360  */
29361 Roo.BorderLayout.create = function(config, targetEl){
29362     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29363     layout.beginUpdate();
29364     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29365     for(var j = 0, jlen = regions.length; j < jlen; j++){
29366         var lr = regions[j];
29367         if(layout.regions[lr] && config[lr].panels){
29368             var r = layout.regions[lr];
29369             var ps = config[lr].panels;
29370             layout.addTypedPanels(r, ps);
29371         }
29372     }
29373     layout.endUpdate();
29374     return layout;
29375 };
29376
29377 // private
29378 Roo.BorderLayout.RegionFactory = {
29379     // private
29380     validRegions : ["north","south","east","west","center"],
29381
29382     // private
29383     create : function(target, mgr, config){
29384         target = target.toLowerCase();
29385         if(config.lightweight || config.basic){
29386             return new Roo.BasicLayoutRegion(mgr, config, target);
29387         }
29388         switch(target){
29389             case "north":
29390                 return new Roo.NorthLayoutRegion(mgr, config);
29391             case "south":
29392                 return new Roo.SouthLayoutRegion(mgr, config);
29393             case "east":
29394                 return new Roo.EastLayoutRegion(mgr, config);
29395             case "west":
29396                 return new Roo.WestLayoutRegion(mgr, config);
29397             case "center":
29398                 return new Roo.CenterLayoutRegion(mgr, config);
29399         }
29400         throw 'Layout region "'+target+'" not supported.';
29401     }
29402 };/*
29403  * Based on:
29404  * Ext JS Library 1.1.1
29405  * Copyright(c) 2006-2007, Ext JS, LLC.
29406  *
29407  * Originally Released Under LGPL - original licence link has changed is not relivant.
29408  *
29409  * Fork - LGPL
29410  * <script type="text/javascript">
29411  */
29412  
29413 /**
29414  * @class Roo.BasicLayoutRegion
29415  * @extends Roo.util.Observable
29416  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29417  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29418  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29419  */
29420 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29421     this.mgr = mgr;
29422     this.position  = pos;
29423     this.events = {
29424         /**
29425          * @scope Roo.BasicLayoutRegion
29426          */
29427         
29428         /**
29429          * @event beforeremove
29430          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29431          * @param {Roo.LayoutRegion} this
29432          * @param {Roo.ContentPanel} panel The panel
29433          * @param {Object} e The cancel event object
29434          */
29435         "beforeremove" : true,
29436         /**
29437          * @event invalidated
29438          * Fires when the layout for this region is changed.
29439          * @param {Roo.LayoutRegion} this
29440          */
29441         "invalidated" : true,
29442         /**
29443          * @event visibilitychange
29444          * Fires when this region is shown or hidden 
29445          * @param {Roo.LayoutRegion} this
29446          * @param {Boolean} visibility true or false
29447          */
29448         "visibilitychange" : true,
29449         /**
29450          * @event paneladded
29451          * Fires when a panel is added. 
29452          * @param {Roo.LayoutRegion} this
29453          * @param {Roo.ContentPanel} panel The panel
29454          */
29455         "paneladded" : true,
29456         /**
29457          * @event panelremoved
29458          * Fires when a panel is removed. 
29459          * @param {Roo.LayoutRegion} this
29460          * @param {Roo.ContentPanel} panel The panel
29461          */
29462         "panelremoved" : true,
29463         /**
29464          * @event collapsed
29465          * Fires when this region is collapsed.
29466          * @param {Roo.LayoutRegion} this
29467          */
29468         "collapsed" : true,
29469         /**
29470          * @event expanded
29471          * Fires when this region is expanded.
29472          * @param {Roo.LayoutRegion} this
29473          */
29474         "expanded" : true,
29475         /**
29476          * @event slideshow
29477          * Fires when this region is slid into view.
29478          * @param {Roo.LayoutRegion} this
29479          */
29480         "slideshow" : true,
29481         /**
29482          * @event slidehide
29483          * Fires when this region slides out of view. 
29484          * @param {Roo.LayoutRegion} this
29485          */
29486         "slidehide" : true,
29487         /**
29488          * @event panelactivated
29489          * Fires when a panel is activated. 
29490          * @param {Roo.LayoutRegion} this
29491          * @param {Roo.ContentPanel} panel The activated panel
29492          */
29493         "panelactivated" : true,
29494         /**
29495          * @event resized
29496          * Fires when the user resizes this region. 
29497          * @param {Roo.LayoutRegion} this
29498          * @param {Number} newSize The new size (width for east/west, height for north/south)
29499          */
29500         "resized" : true
29501     };
29502     /** A collection of panels in this region. @type Roo.util.MixedCollection */
29503     this.panels = new Roo.util.MixedCollection();
29504     this.panels.getKey = this.getPanelId.createDelegate(this);
29505     this.box = null;
29506     this.activePanel = null;
29507     // ensure listeners are added...
29508     
29509     if (config.listeners || config.events) {
29510         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29511             listeners : config.listeners || {},
29512             events : config.events || {}
29513         });
29514     }
29515     
29516     if(skipConfig !== true){
29517         this.applyConfig(config);
29518     }
29519 };
29520
29521 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29522     getPanelId : function(p){
29523         return p.getId();
29524     },
29525     
29526     applyConfig : function(config){
29527         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29528         this.config = config;
29529         
29530     },
29531     
29532     /**
29533      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
29534      * the width, for horizontal (north, south) the height.
29535      * @param {Number} newSize The new width or height
29536      */
29537     resizeTo : function(newSize){
29538         var el = this.el ? this.el :
29539                  (this.activePanel ? this.activePanel.getEl() : null);
29540         if(el){
29541             switch(this.position){
29542                 case "east":
29543                 case "west":
29544                     el.setWidth(newSize);
29545                     this.fireEvent("resized", this, newSize);
29546                 break;
29547                 case "north":
29548                 case "south":
29549                     el.setHeight(newSize);
29550                     this.fireEvent("resized", this, newSize);
29551                 break;                
29552             }
29553         }
29554     },
29555     
29556     getBox : function(){
29557         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29558     },
29559     
29560     getMargins : function(){
29561         return this.margins;
29562     },
29563     
29564     updateBox : function(box){
29565         this.box = box;
29566         var el = this.activePanel.getEl();
29567         el.dom.style.left = box.x + "px";
29568         el.dom.style.top = box.y + "px";
29569         this.activePanel.setSize(box.width, box.height);
29570     },
29571     
29572     /**
29573      * Returns the container element for this region.
29574      * @return {Roo.Element}
29575      */
29576     getEl : function(){
29577         return this.activePanel;
29578     },
29579     
29580     /**
29581      * Returns true if this region is currently visible.
29582      * @return {Boolean}
29583      */
29584     isVisible : function(){
29585         return this.activePanel ? true : false;
29586     },
29587     
29588     setActivePanel : function(panel){
29589         panel = this.getPanel(panel);
29590         if(this.activePanel && this.activePanel != panel){
29591             this.activePanel.setActiveState(false);
29592             this.activePanel.getEl().setLeftTop(-10000,-10000);
29593         }
29594         this.activePanel = panel;
29595         panel.setActiveState(true);
29596         if(this.box){
29597             panel.setSize(this.box.width, this.box.height);
29598         }
29599         this.fireEvent("panelactivated", this, panel);
29600         this.fireEvent("invalidated");
29601     },
29602     
29603     /**
29604      * Show the specified panel.
29605      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29606      * @return {Roo.ContentPanel} The shown panel or null
29607      */
29608     showPanel : function(panel){
29609         if(panel = this.getPanel(panel)){
29610             this.setActivePanel(panel);
29611         }
29612         return panel;
29613     },
29614     
29615     /**
29616      * Get the active panel for this region.
29617      * @return {Roo.ContentPanel} The active panel or null
29618      */
29619     getActivePanel : function(){
29620         return this.activePanel;
29621     },
29622     
29623     /**
29624      * Add the passed ContentPanel(s)
29625      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29626      * @return {Roo.ContentPanel} The panel added (if only one was added)
29627      */
29628     add : function(panel){
29629         if(arguments.length > 1){
29630             for(var i = 0, len = arguments.length; i < len; i++) {
29631                 this.add(arguments[i]);
29632             }
29633             return null;
29634         }
29635         if(this.hasPanel(panel)){
29636             this.showPanel(panel);
29637             return panel;
29638         }
29639         var el = panel.getEl();
29640         if(el.dom.parentNode != this.mgr.el.dom){
29641             this.mgr.el.dom.appendChild(el.dom);
29642         }
29643         if(panel.setRegion){
29644             panel.setRegion(this);
29645         }
29646         this.panels.add(panel);
29647         el.setStyle("position", "absolute");
29648         if(!panel.background){
29649             this.setActivePanel(panel);
29650             if(this.config.initialSize && this.panels.getCount()==1){
29651                 this.resizeTo(this.config.initialSize);
29652             }
29653         }
29654         this.fireEvent("paneladded", this, panel);
29655         return panel;
29656     },
29657     
29658     /**
29659      * Returns true if the panel is in this region.
29660      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29661      * @return {Boolean}
29662      */
29663     hasPanel : function(panel){
29664         if(typeof panel == "object"){ // must be panel obj
29665             panel = panel.getId();
29666         }
29667         return this.getPanel(panel) ? true : false;
29668     },
29669     
29670     /**
29671      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29672      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29673      * @param {Boolean} preservePanel Overrides the config preservePanel option
29674      * @return {Roo.ContentPanel} The panel that was removed
29675      */
29676     remove : function(panel, preservePanel){
29677         panel = this.getPanel(panel);
29678         if(!panel){
29679             return null;
29680         }
29681         var e = {};
29682         this.fireEvent("beforeremove", this, panel, e);
29683         if(e.cancel === true){
29684             return null;
29685         }
29686         var panelId = panel.getId();
29687         this.panels.removeKey(panelId);
29688         return panel;
29689     },
29690     
29691     /**
29692      * Returns the panel specified or null if it's not in this region.
29693      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29694      * @return {Roo.ContentPanel}
29695      */
29696     getPanel : function(id){
29697         if(typeof id == "object"){ // must be panel obj
29698             return id;
29699         }
29700         return this.panels.get(id);
29701     },
29702     
29703     /**
29704      * Returns this regions position (north/south/east/west/center).
29705      * @return {String} 
29706      */
29707     getPosition: function(){
29708         return this.position;    
29709     }
29710 });/*
29711  * Based on:
29712  * Ext JS Library 1.1.1
29713  * Copyright(c) 2006-2007, Ext JS, LLC.
29714  *
29715  * Originally Released Under LGPL - original licence link has changed is not relivant.
29716  *
29717  * Fork - LGPL
29718  * <script type="text/javascript">
29719  */
29720  
29721 /**
29722  * @class Roo.LayoutRegion
29723  * @extends Roo.BasicLayoutRegion
29724  * This class represents a region in a layout manager.
29725  * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
29726  * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
29727  * @cfg {Boolean} floatable False to disable floating (defaults to true)
29728  * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
29729  * @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})
29730  * @cfg {String} tabPosition "top" or "bottom" (defaults to "bottom")
29731  * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
29732  * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
29733  * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
29734  * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
29735  * @cfg {String} title The title for the region (overrides panel titles)
29736  * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
29737  * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
29738  * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
29739  * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
29740  * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
29741  * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
29742  * the space available, similar to FireFox 1.5 tabs (defaults to false)
29743  * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
29744  * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
29745  * @cfg {Boolean} showPin True to show a pin button
29746 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
29747 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
29748 * @cfg {Boolean} disableTabTips True to disable tab tooltips
29749 * @cfg {Number} width  For East/West panels
29750 * @cfg {Number} height For North/South panels
29751 * @cfg {Boolean} split To show the splitter
29752  */
29753 Roo.LayoutRegion = function(mgr, config, pos){
29754     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
29755     var dh = Roo.DomHelper;
29756     /** This region's container element 
29757     * @type Roo.Element */
29758     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
29759     /** This region's title element 
29760     * @type Roo.Element */
29761
29762     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
29763         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
29764         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
29765     ]}, true);
29766     this.titleEl.enableDisplayMode();
29767     /** This region's title text element 
29768     * @type HTMLElement */
29769     this.titleTextEl = this.titleEl.dom.firstChild;
29770     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
29771     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
29772     this.closeBtn.enableDisplayMode();
29773     this.closeBtn.on("click", this.closeClicked, this);
29774     this.closeBtn.hide();
29775
29776     this.createBody(config);
29777     this.visible = true;
29778     this.collapsed = false;
29779
29780     if(config.hideWhenEmpty){
29781         this.hide();
29782         this.on("paneladded", this.validateVisibility, this);
29783         this.on("panelremoved", this.validateVisibility, this);
29784     }
29785     this.applyConfig(config);
29786 };
29787
29788 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
29789
29790     createBody : function(){
29791         /** This region's body element 
29792         * @type Roo.Element */
29793         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
29794     },
29795
29796     applyConfig : function(c){
29797         if(c.collapsible && this.position != "center" && !this.collapsedEl){
29798             var dh = Roo.DomHelper;
29799             if(c.titlebar !== false){
29800                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
29801                 this.collapseBtn.on("click", this.collapse, this);
29802                 this.collapseBtn.enableDisplayMode();
29803
29804                 if(c.showPin === true || this.showPin){
29805                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
29806                     this.stickBtn.enableDisplayMode();
29807                     this.stickBtn.on("click", this.expand, this);
29808                     this.stickBtn.hide();
29809                 }
29810             }
29811             /** This region's collapsed element
29812             * @type Roo.Element */
29813             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
29814                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
29815             ]}, true);
29816             if(c.floatable !== false){
29817                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
29818                this.collapsedEl.on("click", this.collapseClick, this);
29819             }
29820
29821             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
29822                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
29823                    id: "message", unselectable: "on", style:{"float":"left"}});
29824                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
29825              }
29826             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
29827             this.expandBtn.on("click", this.expand, this);
29828         }
29829         if(this.collapseBtn){
29830             this.collapseBtn.setVisible(c.collapsible == true);
29831         }
29832         this.cmargins = c.cmargins || this.cmargins ||
29833                          (this.position == "west" || this.position == "east" ?
29834                              {top: 0, left: 2, right:2, bottom: 0} :
29835                              {top: 2, left: 0, right:0, bottom: 2});
29836         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29837         this.bottomTabs = c.tabPosition != "top";
29838         this.autoScroll = c.autoScroll || false;
29839         if(this.autoScroll){
29840             this.bodyEl.setStyle("overflow", "auto");
29841         }else{
29842             this.bodyEl.setStyle("overflow", "hidden");
29843         }
29844         //if(c.titlebar !== false){
29845             if((!c.titlebar && !c.title) || c.titlebar === false){
29846                 this.titleEl.hide();
29847             }else{
29848                 this.titleEl.show();
29849                 if(c.title){
29850                     this.titleTextEl.innerHTML = c.title;
29851                 }
29852             }
29853         //}
29854         this.duration = c.duration || .30;
29855         this.slideDuration = c.slideDuration || .45;
29856         this.config = c;
29857         if(c.collapsed){
29858             this.collapse(true);
29859         }
29860         if(c.hidden){
29861             this.hide();
29862         }
29863     },
29864     /**
29865      * Returns true if this region is currently visible.
29866      * @return {Boolean}
29867      */
29868     isVisible : function(){
29869         return this.visible;
29870     },
29871
29872     /**
29873      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
29874      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
29875      */
29876     setCollapsedTitle : function(title){
29877         title = title || "&#160;";
29878         if(this.collapsedTitleTextEl){
29879             this.collapsedTitleTextEl.innerHTML = title;
29880         }
29881     },
29882
29883     getBox : function(){
29884         var b;
29885         if(!this.collapsed){
29886             b = this.el.getBox(false, true);
29887         }else{
29888             b = this.collapsedEl.getBox(false, true);
29889         }
29890         return b;
29891     },
29892
29893     getMargins : function(){
29894         return this.collapsed ? this.cmargins : this.margins;
29895     },
29896
29897     highlight : function(){
29898         this.el.addClass("x-layout-panel-dragover");
29899     },
29900
29901     unhighlight : function(){
29902         this.el.removeClass("x-layout-panel-dragover");
29903     },
29904
29905     updateBox : function(box){
29906         this.box = box;
29907         if(!this.collapsed){
29908             this.el.dom.style.left = box.x + "px";
29909             this.el.dom.style.top = box.y + "px";
29910             this.updateBody(box.width, box.height);
29911         }else{
29912             this.collapsedEl.dom.style.left = box.x + "px";
29913             this.collapsedEl.dom.style.top = box.y + "px";
29914             this.collapsedEl.setSize(box.width, box.height);
29915         }
29916         if(this.tabs){
29917             this.tabs.autoSizeTabs();
29918         }
29919     },
29920
29921     updateBody : function(w, h){
29922         if(w !== null){
29923             this.el.setWidth(w);
29924             w -= this.el.getBorderWidth("rl");
29925             if(this.config.adjustments){
29926                 w += this.config.adjustments[0];
29927             }
29928         }
29929         if(h !== null){
29930             this.el.setHeight(h);
29931             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
29932             h -= this.el.getBorderWidth("tb");
29933             if(this.config.adjustments){
29934                 h += this.config.adjustments[1];
29935             }
29936             this.bodyEl.setHeight(h);
29937             if(this.tabs){
29938                 h = this.tabs.syncHeight(h);
29939             }
29940         }
29941         if(this.panelSize){
29942             w = w !== null ? w : this.panelSize.width;
29943             h = h !== null ? h : this.panelSize.height;
29944         }
29945         if(this.activePanel){
29946             var el = this.activePanel.getEl();
29947             w = w !== null ? w : el.getWidth();
29948             h = h !== null ? h : el.getHeight();
29949             this.panelSize = {width: w, height: h};
29950             this.activePanel.setSize(w, h);
29951         }
29952         if(Roo.isIE && this.tabs){
29953             this.tabs.el.repaint();
29954         }
29955     },
29956
29957     /**
29958      * Returns the container element for this region.
29959      * @return {Roo.Element}
29960      */
29961     getEl : function(){
29962         return this.el;
29963     },
29964
29965     /**
29966      * Hides this region.
29967      */
29968     hide : function(){
29969         if(!this.collapsed){
29970             this.el.dom.style.left = "-2000px";
29971             this.el.hide();
29972         }else{
29973             this.collapsedEl.dom.style.left = "-2000px";
29974             this.collapsedEl.hide();
29975         }
29976         this.visible = false;
29977         this.fireEvent("visibilitychange", this, false);
29978     },
29979
29980     /**
29981      * Shows this region if it was previously hidden.
29982      */
29983     show : function(){
29984         if(!this.collapsed){
29985             this.el.show();
29986         }else{
29987             this.collapsedEl.show();
29988         }
29989         this.visible = true;
29990         this.fireEvent("visibilitychange", this, true);
29991     },
29992
29993     closeClicked : function(){
29994         if(this.activePanel){
29995             this.remove(this.activePanel);
29996         }
29997     },
29998
29999     collapseClick : function(e){
30000         if(this.isSlid){
30001            e.stopPropagation();
30002            this.slideIn();
30003         }else{
30004            e.stopPropagation();
30005            this.slideOut();
30006         }
30007     },
30008
30009     /**
30010      * Collapses this region.
30011      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
30012      */
30013     collapse : function(skipAnim){
30014         if(this.collapsed) return;
30015         this.collapsed = true;
30016         if(this.split){
30017             this.split.el.hide();
30018         }
30019         if(this.config.animate && skipAnim !== true){
30020             this.fireEvent("invalidated", this);
30021             this.animateCollapse();
30022         }else{
30023             this.el.setLocation(-20000,-20000);
30024             this.el.hide();
30025             this.collapsedEl.show();
30026             this.fireEvent("collapsed", this);
30027             this.fireEvent("invalidated", this);
30028         }
30029     },
30030
30031     animateCollapse : function(){
30032         // overridden
30033     },
30034
30035     /**
30036      * Expands this region if it was previously collapsed.
30037      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
30038      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
30039      */
30040     expand : function(e, skipAnim){
30041         if(e) e.stopPropagation();
30042         if(!this.collapsed || this.el.hasActiveFx()) return;
30043         if(this.isSlid){
30044             this.afterSlideIn();
30045             skipAnim = true;
30046         }
30047         this.collapsed = false;
30048         if(this.config.animate && skipAnim !== true){
30049             this.animateExpand();
30050         }else{
30051             this.el.show();
30052             if(this.split){
30053                 this.split.el.show();
30054             }
30055             this.collapsedEl.setLocation(-2000,-2000);
30056             this.collapsedEl.hide();
30057             this.fireEvent("invalidated", this);
30058             this.fireEvent("expanded", this);
30059         }
30060     },
30061
30062     animateExpand : function(){
30063         // overridden
30064     },
30065
30066     initTabs : function(){
30067         this.bodyEl.setStyle("overflow", "hidden");
30068         var ts = new Roo.TabPanel(this.bodyEl.dom, {
30069             tabPosition: this.bottomTabs ? 'bottom' : 'top',
30070             disableTooltips: this.config.disableTabTips
30071         });
30072         if(this.config.hideTabs){
30073             ts.stripWrap.setDisplayed(false);
30074         }
30075         this.tabs = ts;
30076         ts.resizeTabs = this.config.resizeTabs === true;
30077         ts.minTabWidth = this.config.minTabWidth || 40;
30078         ts.maxTabWidth = this.config.maxTabWidth || 250;
30079         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
30080         ts.monitorResize = false;
30081         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30082         ts.bodyEl.addClass('x-layout-tabs-body');
30083         this.panels.each(this.initPanelAsTab, this);
30084     },
30085
30086     initPanelAsTab : function(panel){
30087         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
30088                     this.config.closeOnTab && panel.isClosable());
30089         if(panel.tabTip !== undefined){
30090             ti.setTooltip(panel.tabTip);
30091         }
30092         ti.on("activate", function(){
30093               this.setActivePanel(panel);
30094         }, this);
30095         if(this.config.closeOnTab){
30096             ti.on("beforeclose", function(t, e){
30097                 e.cancel = true;
30098                 this.remove(panel);
30099             }, this);
30100         }
30101         return ti;
30102     },
30103
30104     updatePanelTitle : function(panel, title){
30105         if(this.activePanel == panel){
30106             this.updateTitle(title);
30107         }
30108         if(this.tabs){
30109             var ti = this.tabs.getTab(panel.getEl().id);
30110             ti.setText(title);
30111             if(panel.tabTip !== undefined){
30112                 ti.setTooltip(panel.tabTip);
30113             }
30114         }
30115     },
30116
30117     updateTitle : function(title){
30118         if(this.titleTextEl && !this.config.title){
30119             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
30120         }
30121     },
30122
30123     setActivePanel : function(panel){
30124         panel = this.getPanel(panel);
30125         if(this.activePanel && this.activePanel != panel){
30126             this.activePanel.setActiveState(false);
30127         }
30128         this.activePanel = panel;
30129         panel.setActiveState(true);
30130         if(this.panelSize){
30131             panel.setSize(this.panelSize.width, this.panelSize.height);
30132         }
30133         if(this.closeBtn){
30134             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30135         }
30136         this.updateTitle(panel.getTitle());
30137         if(this.tabs){
30138             this.fireEvent("invalidated", this);
30139         }
30140         this.fireEvent("panelactivated", this, panel);
30141     },
30142
30143     /**
30144      * Shows the specified panel.
30145      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30146      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30147      */
30148     showPanel : function(panel){
30149         if(panel = this.getPanel(panel)){
30150             if(this.tabs){
30151                 var tab = this.tabs.getTab(panel.getEl().id);
30152                 if(tab.isHidden()){
30153                     this.tabs.unhideTab(tab.id);
30154                 }
30155                 tab.activate();
30156             }else{
30157                 this.setActivePanel(panel);
30158             }
30159         }
30160         return panel;
30161     },
30162
30163     /**
30164      * Get the active panel for this region.
30165      * @return {Roo.ContentPanel} The active panel or null
30166      */
30167     getActivePanel : function(){
30168         return this.activePanel;
30169     },
30170
30171     validateVisibility : function(){
30172         if(this.panels.getCount() < 1){
30173             this.updateTitle("&#160;");
30174             this.closeBtn.hide();
30175             this.hide();
30176         }else{
30177             if(!this.isVisible()){
30178                 this.show();
30179             }
30180         }
30181     },
30182
30183     /**
30184      * Adds the passed ContentPanel(s) to this region.
30185      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30186      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30187      */
30188     add : function(panel){
30189         if(arguments.length > 1){
30190             for(var i = 0, len = arguments.length; i < len; i++) {
30191                 this.add(arguments[i]);
30192             }
30193             return null;
30194         }
30195         if(this.hasPanel(panel)){
30196             this.showPanel(panel);
30197             return panel;
30198         }
30199         panel.setRegion(this);
30200         this.panels.add(panel);
30201         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30202             this.bodyEl.dom.appendChild(panel.getEl().dom);
30203             if(panel.background !== true){
30204                 this.setActivePanel(panel);
30205             }
30206             this.fireEvent("paneladded", this, panel);
30207             return panel;
30208         }
30209         if(!this.tabs){
30210             this.initTabs();
30211         }else{
30212             this.initPanelAsTab(panel);
30213         }
30214         if(panel.background !== true){
30215             this.tabs.activate(panel.getEl().id);
30216         }
30217         this.fireEvent("paneladded", this, panel);
30218         return panel;
30219     },
30220
30221     /**
30222      * Hides the tab for the specified panel.
30223      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30224      */
30225     hidePanel : function(panel){
30226         if(this.tabs && (panel = this.getPanel(panel))){
30227             this.tabs.hideTab(panel.getEl().id);
30228         }
30229     },
30230
30231     /**
30232      * Unhides the tab for a previously hidden panel.
30233      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30234      */
30235     unhidePanel : function(panel){
30236         if(this.tabs && (panel = this.getPanel(panel))){
30237             this.tabs.unhideTab(panel.getEl().id);
30238         }
30239     },
30240
30241     clearPanels : function(){
30242         while(this.panels.getCount() > 0){
30243              this.remove(this.panels.first());
30244         }
30245     },
30246
30247     /**
30248      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30249      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30250      * @param {Boolean} preservePanel Overrides the config preservePanel option
30251      * @return {Roo.ContentPanel} The panel that was removed
30252      */
30253     remove : function(panel, preservePanel){
30254         panel = this.getPanel(panel);
30255         if(!panel){
30256             return null;
30257         }
30258         var e = {};
30259         this.fireEvent("beforeremove", this, panel, e);
30260         if(e.cancel === true){
30261             return null;
30262         }
30263         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30264         var panelId = panel.getId();
30265         this.panels.removeKey(panelId);
30266         if(preservePanel){
30267             document.body.appendChild(panel.getEl().dom);
30268         }
30269         if(this.tabs){
30270             this.tabs.removeTab(panel.getEl().id);
30271         }else if (!preservePanel){
30272             this.bodyEl.dom.removeChild(panel.getEl().dom);
30273         }
30274         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30275             var p = this.panels.first();
30276             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30277             tempEl.appendChild(p.getEl().dom);
30278             this.bodyEl.update("");
30279             this.bodyEl.dom.appendChild(p.getEl().dom);
30280             tempEl = null;
30281             this.updateTitle(p.getTitle());
30282             this.tabs = null;
30283             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30284             this.setActivePanel(p);
30285         }
30286         panel.setRegion(null);
30287         if(this.activePanel == panel){
30288             this.activePanel = null;
30289         }
30290         if(this.config.autoDestroy !== false && preservePanel !== true){
30291             try{panel.destroy();}catch(e){}
30292         }
30293         this.fireEvent("panelremoved", this, panel);
30294         return panel;
30295     },
30296
30297     /**
30298      * Returns the TabPanel component used by this region
30299      * @return {Roo.TabPanel}
30300      */
30301     getTabs : function(){
30302         return this.tabs;
30303     },
30304
30305     createTool : function(parentEl, className){
30306         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30307             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30308         btn.addClassOnOver("x-layout-tools-button-over");
30309         return btn;
30310     }
30311 });/*
30312  * Based on:
30313  * Ext JS Library 1.1.1
30314  * Copyright(c) 2006-2007, Ext JS, LLC.
30315  *
30316  * Originally Released Under LGPL - original licence link has changed is not relivant.
30317  *
30318  * Fork - LGPL
30319  * <script type="text/javascript">
30320  */
30321  
30322
30323
30324 /**
30325  * @class Roo.SplitLayoutRegion
30326  * @extends Roo.LayoutRegion
30327  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30328  */
30329 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30330     this.cursor = cursor;
30331     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30332 };
30333
30334 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30335     splitTip : "Drag to resize.",
30336     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30337     useSplitTips : false,
30338
30339     applyConfig : function(config){
30340         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30341         if(config.split){
30342             if(!this.split){
30343                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30344                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30345                 /** The SplitBar for this region 
30346                 * @type Roo.SplitBar */
30347                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30348                 this.split.on("moved", this.onSplitMove, this);
30349                 this.split.useShim = config.useShim === true;
30350                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30351                 if(this.useSplitTips){
30352                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30353                 }
30354                 if(config.collapsible){
30355                     this.split.el.on("dblclick", this.collapse,  this);
30356                 }
30357             }
30358             if(typeof config.minSize != "undefined"){
30359                 this.split.minSize = config.minSize;
30360             }
30361             if(typeof config.maxSize != "undefined"){
30362                 this.split.maxSize = config.maxSize;
30363             }
30364             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30365                 this.hideSplitter();
30366             }
30367         }
30368     },
30369
30370     getHMaxSize : function(){
30371          var cmax = this.config.maxSize || 10000;
30372          var center = this.mgr.getRegion("center");
30373          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30374     },
30375
30376     getVMaxSize : function(){
30377          var cmax = this.config.maxSize || 10000;
30378          var center = this.mgr.getRegion("center");
30379          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30380     },
30381
30382     onSplitMove : function(split, newSize){
30383         this.fireEvent("resized", this, newSize);
30384     },
30385     
30386     /** 
30387      * Returns the {@link Roo.SplitBar} for this region.
30388      * @return {Roo.SplitBar}
30389      */
30390     getSplitBar : function(){
30391         return this.split;
30392     },
30393     
30394     hide : function(){
30395         this.hideSplitter();
30396         Roo.SplitLayoutRegion.superclass.hide.call(this);
30397     },
30398
30399     hideSplitter : function(){
30400         if(this.split){
30401             this.split.el.setLocation(-2000,-2000);
30402             this.split.el.hide();
30403         }
30404     },
30405
30406     show : function(){
30407         if(this.split){
30408             this.split.el.show();
30409         }
30410         Roo.SplitLayoutRegion.superclass.show.call(this);
30411     },
30412     
30413     beforeSlide: function(){
30414         if(Roo.isGecko){// firefox overflow auto bug workaround
30415             this.bodyEl.clip();
30416             if(this.tabs) this.tabs.bodyEl.clip();
30417             if(this.activePanel){
30418                 this.activePanel.getEl().clip();
30419                 
30420                 if(this.activePanel.beforeSlide){
30421                     this.activePanel.beforeSlide();
30422                 }
30423             }
30424         }
30425     },
30426     
30427     afterSlide : function(){
30428         if(Roo.isGecko){// firefox overflow auto bug workaround
30429             this.bodyEl.unclip();
30430             if(this.tabs) this.tabs.bodyEl.unclip();
30431             if(this.activePanel){
30432                 this.activePanel.getEl().unclip();
30433                 if(this.activePanel.afterSlide){
30434                     this.activePanel.afterSlide();
30435                 }
30436             }
30437         }
30438     },
30439
30440     initAutoHide : function(){
30441         if(this.autoHide !== false){
30442             if(!this.autoHideHd){
30443                 var st = new Roo.util.DelayedTask(this.slideIn, this);
30444                 this.autoHideHd = {
30445                     "mouseout": function(e){
30446                         if(!e.within(this.el, true)){
30447                             st.delay(500);
30448                         }
30449                     },
30450                     "mouseover" : function(e){
30451                         st.cancel();
30452                     },
30453                     scope : this
30454                 };
30455             }
30456             this.el.on(this.autoHideHd);
30457         }
30458     },
30459
30460     clearAutoHide : function(){
30461         if(this.autoHide !== false){
30462             this.el.un("mouseout", this.autoHideHd.mouseout);
30463             this.el.un("mouseover", this.autoHideHd.mouseover);
30464         }
30465     },
30466
30467     clearMonitor : function(){
30468         Roo.get(document).un("click", this.slideInIf, this);
30469     },
30470
30471     // these names are backwards but not changed for compat
30472     slideOut : function(){
30473         if(this.isSlid || this.el.hasActiveFx()){
30474             return;
30475         }
30476         this.isSlid = true;
30477         if(this.collapseBtn){
30478             this.collapseBtn.hide();
30479         }
30480         this.closeBtnState = this.closeBtn.getStyle('display');
30481         this.closeBtn.hide();
30482         if(this.stickBtn){
30483             this.stickBtn.show();
30484         }
30485         this.el.show();
30486         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30487         this.beforeSlide();
30488         this.el.setStyle("z-index", 10001);
30489         this.el.slideIn(this.getSlideAnchor(), {
30490             callback: function(){
30491                 this.afterSlide();
30492                 this.initAutoHide();
30493                 Roo.get(document).on("click", this.slideInIf, this);
30494                 this.fireEvent("slideshow", this);
30495             },
30496             scope: this,
30497             block: true
30498         });
30499     },
30500
30501     afterSlideIn : function(){
30502         this.clearAutoHide();
30503         this.isSlid = false;
30504         this.clearMonitor();
30505         this.el.setStyle("z-index", "");
30506         if(this.collapseBtn){
30507             this.collapseBtn.show();
30508         }
30509         this.closeBtn.setStyle('display', this.closeBtnState);
30510         if(this.stickBtn){
30511             this.stickBtn.hide();
30512         }
30513         this.fireEvent("slidehide", this);
30514     },
30515
30516     slideIn : function(cb){
30517         if(!this.isSlid || this.el.hasActiveFx()){
30518             Roo.callback(cb);
30519             return;
30520         }
30521         this.isSlid = false;
30522         this.beforeSlide();
30523         this.el.slideOut(this.getSlideAnchor(), {
30524             callback: function(){
30525                 this.el.setLeftTop(-10000, -10000);
30526                 this.afterSlide();
30527                 this.afterSlideIn();
30528                 Roo.callback(cb);
30529             },
30530             scope: this,
30531             block: true
30532         });
30533     },
30534     
30535     slideInIf : function(e){
30536         if(!e.within(this.el)){
30537             this.slideIn();
30538         }
30539     },
30540
30541     animateCollapse : function(){
30542         this.beforeSlide();
30543         this.el.setStyle("z-index", 20000);
30544         var anchor = this.getSlideAnchor();
30545         this.el.slideOut(anchor, {
30546             callback : function(){
30547                 this.el.setStyle("z-index", "");
30548                 this.collapsedEl.slideIn(anchor, {duration:.3});
30549                 this.afterSlide();
30550                 this.el.setLocation(-10000,-10000);
30551                 this.el.hide();
30552                 this.fireEvent("collapsed", this);
30553             },
30554             scope: this,
30555             block: true
30556         });
30557     },
30558
30559     animateExpand : function(){
30560         this.beforeSlide();
30561         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30562         this.el.setStyle("z-index", 20000);
30563         this.collapsedEl.hide({
30564             duration:.1
30565         });
30566         this.el.slideIn(this.getSlideAnchor(), {
30567             callback : function(){
30568                 this.el.setStyle("z-index", "");
30569                 this.afterSlide();
30570                 if(this.split){
30571                     this.split.el.show();
30572                 }
30573                 this.fireEvent("invalidated", this);
30574                 this.fireEvent("expanded", this);
30575             },
30576             scope: this,
30577             block: true
30578         });
30579     },
30580
30581     anchors : {
30582         "west" : "left",
30583         "east" : "right",
30584         "north" : "top",
30585         "south" : "bottom"
30586     },
30587
30588     sanchors : {
30589         "west" : "l",
30590         "east" : "r",
30591         "north" : "t",
30592         "south" : "b"
30593     },
30594
30595     canchors : {
30596         "west" : "tl-tr",
30597         "east" : "tr-tl",
30598         "north" : "tl-bl",
30599         "south" : "bl-tl"
30600     },
30601
30602     getAnchor : function(){
30603         return this.anchors[this.position];
30604     },
30605
30606     getCollapseAnchor : function(){
30607         return this.canchors[this.position];
30608     },
30609
30610     getSlideAnchor : function(){
30611         return this.sanchors[this.position];
30612     },
30613
30614     getAlignAdj : function(){
30615         var cm = this.cmargins;
30616         switch(this.position){
30617             case "west":
30618                 return [0, 0];
30619             break;
30620             case "east":
30621                 return [0, 0];
30622             break;
30623             case "north":
30624                 return [0, 0];
30625             break;
30626             case "south":
30627                 return [0, 0];
30628             break;
30629         }
30630     },
30631
30632     getExpandAdj : function(){
30633         var c = this.collapsedEl, cm = this.cmargins;
30634         switch(this.position){
30635             case "west":
30636                 return [-(cm.right+c.getWidth()+cm.left), 0];
30637             break;
30638             case "east":
30639                 return [cm.right+c.getWidth()+cm.left, 0];
30640             break;
30641             case "north":
30642                 return [0, -(cm.top+cm.bottom+c.getHeight())];
30643             break;
30644             case "south":
30645                 return [0, cm.top+cm.bottom+c.getHeight()];
30646             break;
30647         }
30648     }
30649 });/*
30650  * Based on:
30651  * Ext JS Library 1.1.1
30652  * Copyright(c) 2006-2007, Ext JS, LLC.
30653  *
30654  * Originally Released Under LGPL - original licence link has changed is not relivant.
30655  *
30656  * Fork - LGPL
30657  * <script type="text/javascript">
30658  */
30659 /*
30660  * These classes are private internal classes
30661  */
30662 Roo.CenterLayoutRegion = function(mgr, config){
30663     Roo.LayoutRegion.call(this, mgr, config, "center");
30664     this.visible = true;
30665     this.minWidth = config.minWidth || 20;
30666     this.minHeight = config.minHeight || 20;
30667 };
30668
30669 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
30670     hide : function(){
30671         // center panel can't be hidden
30672     },
30673     
30674     show : function(){
30675         // center panel can't be hidden
30676     },
30677     
30678     getMinWidth: function(){
30679         return this.minWidth;
30680     },
30681     
30682     getMinHeight: function(){
30683         return this.minHeight;
30684     }
30685 });
30686
30687
30688 Roo.NorthLayoutRegion = function(mgr, config){
30689     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
30690     if(this.split){
30691         this.split.placement = Roo.SplitBar.TOP;
30692         this.split.orientation = Roo.SplitBar.VERTICAL;
30693         this.split.el.addClass("x-layout-split-v");
30694     }
30695     var size = config.initialSize || config.height;
30696     if(typeof size != "undefined"){
30697         this.el.setHeight(size);
30698     }
30699 };
30700 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
30701     orientation: Roo.SplitBar.VERTICAL,
30702     getBox : function(){
30703         if(this.collapsed){
30704             return this.collapsedEl.getBox();
30705         }
30706         var box = this.el.getBox();
30707         if(this.split){
30708             box.height += this.split.el.getHeight();
30709         }
30710         return box;
30711     },
30712     
30713     updateBox : function(box){
30714         if(this.split && !this.collapsed){
30715             box.height -= this.split.el.getHeight();
30716             this.split.el.setLeft(box.x);
30717             this.split.el.setTop(box.y+box.height);
30718             this.split.el.setWidth(box.width);
30719         }
30720         if(this.collapsed){
30721             this.updateBody(box.width, null);
30722         }
30723         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30724     }
30725 });
30726
30727 Roo.SouthLayoutRegion = function(mgr, config){
30728     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
30729     if(this.split){
30730         this.split.placement = Roo.SplitBar.BOTTOM;
30731         this.split.orientation = Roo.SplitBar.VERTICAL;
30732         this.split.el.addClass("x-layout-split-v");
30733     }
30734     var size = config.initialSize || config.height;
30735     if(typeof size != "undefined"){
30736         this.el.setHeight(size);
30737     }
30738 };
30739 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
30740     orientation: Roo.SplitBar.VERTICAL,
30741     getBox : function(){
30742         if(this.collapsed){
30743             return this.collapsedEl.getBox();
30744         }
30745         var box = this.el.getBox();
30746         if(this.split){
30747             var sh = this.split.el.getHeight();
30748             box.height += sh;
30749             box.y -= sh;
30750         }
30751         return box;
30752     },
30753     
30754     updateBox : function(box){
30755         if(this.split && !this.collapsed){
30756             var sh = this.split.el.getHeight();
30757             box.height -= sh;
30758             box.y += sh;
30759             this.split.el.setLeft(box.x);
30760             this.split.el.setTop(box.y-sh);
30761             this.split.el.setWidth(box.width);
30762         }
30763         if(this.collapsed){
30764             this.updateBody(box.width, null);
30765         }
30766         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30767     }
30768 });
30769
30770 Roo.EastLayoutRegion = function(mgr, config){
30771     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
30772     if(this.split){
30773         this.split.placement = Roo.SplitBar.RIGHT;
30774         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30775         this.split.el.addClass("x-layout-split-h");
30776     }
30777     var size = config.initialSize || config.width;
30778     if(typeof size != "undefined"){
30779         this.el.setWidth(size);
30780     }
30781 };
30782 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
30783     orientation: Roo.SplitBar.HORIZONTAL,
30784     getBox : function(){
30785         if(this.collapsed){
30786             return this.collapsedEl.getBox();
30787         }
30788         var box = this.el.getBox();
30789         if(this.split){
30790             var sw = this.split.el.getWidth();
30791             box.width += sw;
30792             box.x -= sw;
30793         }
30794         return box;
30795     },
30796
30797     updateBox : function(box){
30798         if(this.split && !this.collapsed){
30799             var sw = this.split.el.getWidth();
30800             box.width -= sw;
30801             this.split.el.setLeft(box.x);
30802             this.split.el.setTop(box.y);
30803             this.split.el.setHeight(box.height);
30804             box.x += sw;
30805         }
30806         if(this.collapsed){
30807             this.updateBody(null, box.height);
30808         }
30809         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30810     }
30811 });
30812
30813 Roo.WestLayoutRegion = function(mgr, config){
30814     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
30815     if(this.split){
30816         this.split.placement = Roo.SplitBar.LEFT;
30817         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30818         this.split.el.addClass("x-layout-split-h");
30819     }
30820     var size = config.initialSize || config.width;
30821     if(typeof size != "undefined"){
30822         this.el.setWidth(size);
30823     }
30824 };
30825 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
30826     orientation: Roo.SplitBar.HORIZONTAL,
30827     getBox : function(){
30828         if(this.collapsed){
30829             return this.collapsedEl.getBox();
30830         }
30831         var box = this.el.getBox();
30832         if(this.split){
30833             box.width += this.split.el.getWidth();
30834         }
30835         return box;
30836     },
30837     
30838     updateBox : function(box){
30839         if(this.split && !this.collapsed){
30840             var sw = this.split.el.getWidth();
30841             box.width -= sw;
30842             this.split.el.setLeft(box.x+box.width);
30843             this.split.el.setTop(box.y);
30844             this.split.el.setHeight(box.height);
30845         }
30846         if(this.collapsed){
30847             this.updateBody(null, box.height);
30848         }
30849         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30850     }
30851 });
30852 /*
30853  * Based on:
30854  * Ext JS Library 1.1.1
30855  * Copyright(c) 2006-2007, Ext JS, LLC.
30856  *
30857  * Originally Released Under LGPL - original licence link has changed is not relivant.
30858  *
30859  * Fork - LGPL
30860  * <script type="text/javascript">
30861  */
30862  
30863  
30864 /*
30865  * Private internal class for reading and applying state
30866  */
30867 Roo.LayoutStateManager = function(layout){
30868      // default empty state
30869      this.state = {
30870         north: {},
30871         south: {},
30872         east: {},
30873         west: {}       
30874     };
30875 };
30876
30877 Roo.LayoutStateManager.prototype = {
30878     init : function(layout, provider){
30879         this.provider = provider;
30880         var state = provider.get(layout.id+"-layout-state");
30881         if(state){
30882             var wasUpdating = layout.isUpdating();
30883             if(!wasUpdating){
30884                 layout.beginUpdate();
30885             }
30886             for(var key in state){
30887                 if(typeof state[key] != "function"){
30888                     var rstate = state[key];
30889                     var r = layout.getRegion(key);
30890                     if(r && rstate){
30891                         if(rstate.size){
30892                             r.resizeTo(rstate.size);
30893                         }
30894                         if(rstate.collapsed == true){
30895                             r.collapse(true);
30896                         }else{
30897                             r.expand(null, true);
30898                         }
30899                     }
30900                 }
30901             }
30902             if(!wasUpdating){
30903                 layout.endUpdate();
30904             }
30905             this.state = state; 
30906         }
30907         this.layout = layout;
30908         layout.on("regionresized", this.onRegionResized, this);
30909         layout.on("regioncollapsed", this.onRegionCollapsed, this);
30910         layout.on("regionexpanded", this.onRegionExpanded, this);
30911     },
30912     
30913     storeState : function(){
30914         this.provider.set(this.layout.id+"-layout-state", this.state);
30915     },
30916     
30917     onRegionResized : function(region, newSize){
30918         this.state[region.getPosition()].size = newSize;
30919         this.storeState();
30920     },
30921     
30922     onRegionCollapsed : function(region){
30923         this.state[region.getPosition()].collapsed = true;
30924         this.storeState();
30925     },
30926     
30927     onRegionExpanded : function(region){
30928         this.state[region.getPosition()].collapsed = false;
30929         this.storeState();
30930     }
30931 };/*
30932  * Based on:
30933  * Ext JS Library 1.1.1
30934  * Copyright(c) 2006-2007, Ext JS, LLC.
30935  *
30936  * Originally Released Under LGPL - original licence link has changed is not relivant.
30937  *
30938  * Fork - LGPL
30939  * <script type="text/javascript">
30940  */
30941 /**
30942  * @class Roo.ContentPanel
30943  * @extends Roo.util.Observable
30944  * A basic ContentPanel element.
30945  * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes  (defaults to false)
30946  * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
30947  * @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
30948  * @cfg {Boolean} closable True if the panel can be closed/removed
30949  * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
30950  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
30951  * @cfg {Toolbar} toolbar A toolbar for this panel
30952  * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
30953  * @cfg {String} title The title for this panel
30954  * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
30955  * @cfg {String} url Calls {@link #setUrl} with this value
30956  * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
30957  * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
30958  * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
30959  * @constructor
30960  * Create a new ContentPanel.
30961  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
30962  * @param {String/Object} config A string to set only the title or a config object
30963  * @param {String} content (optional) Set the HTML content for this panel
30964  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
30965  */
30966 Roo.ContentPanel = function(el, config, content){
30967     
30968      
30969     /*
30970     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
30971         config = el;
30972         el = Roo.id();
30973     }
30974     if (config && config.parentLayout) { 
30975         el = config.parentLayout.el.createChild(); 
30976     }
30977     */
30978     if(el.autoCreate){ // xtype is available if this is called from factory
30979         config = el;
30980         el = Roo.id();
30981     }
30982     this.el = Roo.get(el);
30983     if(!this.el && config && config.autoCreate){
30984         if(typeof config.autoCreate == "object"){
30985             if(!config.autoCreate.id){
30986                 config.autoCreate.id = config.id||el;
30987             }
30988             this.el = Roo.DomHelper.append(document.body,
30989                         config.autoCreate, true);
30990         }else{
30991             this.el = Roo.DomHelper.append(document.body,
30992                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
30993         }
30994     }
30995     this.closable = false;
30996     this.loaded = false;
30997     this.active = false;
30998     if(typeof config == "string"){
30999         this.title = config;
31000     }else{
31001         Roo.apply(this, config);
31002     }
31003     
31004     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
31005         this.wrapEl = this.el.wrap();    
31006         this.toolbar = new Roo.Toolbar(this.el.insertSibling(false, 'before'), [] , this.toolbar);
31007         
31008     }
31009     
31010     
31011     
31012     if(this.resizeEl){
31013         this.resizeEl = Roo.get(this.resizeEl, true);
31014     }else{
31015         this.resizeEl = this.el;
31016     }
31017     this.addEvents({
31018         /**
31019          * @event activate
31020          * Fires when this panel is activated. 
31021          * @param {Roo.ContentPanel} this
31022          */
31023         "activate" : true,
31024         /**
31025          * @event deactivate
31026          * Fires when this panel is activated. 
31027          * @param {Roo.ContentPanel} this
31028          */
31029         "deactivate" : true,
31030
31031         /**
31032          * @event resize
31033          * Fires when this panel is resized if fitToFrame is true.
31034          * @param {Roo.ContentPanel} this
31035          * @param {Number} width The width after any component adjustments
31036          * @param {Number} height The height after any component adjustments
31037          */
31038         "resize" : true
31039     });
31040     if(this.autoScroll){
31041         this.resizeEl.setStyle("overflow", "auto");
31042     } else {
31043         // fix randome scrolling
31044         this.el.on('scroll', function() {
31045             Roo.log('fix random scolling');
31046             this.scrollTo('top',0); 
31047         });
31048     }
31049     content = content || this.content;
31050     if(content){
31051         this.setContent(content);
31052     }
31053     if(config && config.url){
31054         this.setUrl(this.url, this.params, this.loadOnce);
31055     }
31056     
31057     
31058     
31059     Roo.ContentPanel.superclass.constructor.call(this);
31060 };
31061
31062 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
31063     tabTip:'',
31064     setRegion : function(region){
31065         this.region = region;
31066         if(region){
31067            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
31068         }else{
31069            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
31070         } 
31071     },
31072     
31073     /**
31074      * Returns the toolbar for this Panel if one was configured. 
31075      * @return {Roo.Toolbar} 
31076      */
31077     getToolbar : function(){
31078         return this.toolbar;
31079     },
31080     
31081     setActiveState : function(active){
31082         this.active = active;
31083         if(!active){
31084             this.fireEvent("deactivate", this);
31085         }else{
31086             this.fireEvent("activate", this);
31087         }
31088     },
31089     /**
31090      * Updates this panel's element
31091      * @param {String} content The new content
31092      * @param {Boolean} loadScripts (optional) true to look for and process scripts
31093     */
31094     setContent : function(content, loadScripts){
31095         this.el.update(content, loadScripts);
31096     },
31097
31098     ignoreResize : function(w, h){
31099         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
31100             return true;
31101         }else{
31102             this.lastSize = {width: w, height: h};
31103             return false;
31104         }
31105     },
31106     /**
31107      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
31108      * @return {Roo.UpdateManager} The UpdateManager
31109      */
31110     getUpdateManager : function(){
31111         return this.el.getUpdateManager();
31112     },
31113      /**
31114      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31115      * @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:
31116 <pre><code>
31117 panel.load({
31118     url: "your-url.php",
31119     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31120     callback: yourFunction,
31121     scope: yourObject, //(optional scope)
31122     discardUrl: false,
31123     nocache: false,
31124     text: "Loading...",
31125     timeout: 30,
31126     scripts: false
31127 });
31128 </code></pre>
31129      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31130      * 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.
31131      * @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}
31132      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31133      * @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.
31134      * @return {Roo.ContentPanel} this
31135      */
31136     load : function(){
31137         var um = this.el.getUpdateManager();
31138         um.update.apply(um, arguments);
31139         return this;
31140     },
31141
31142
31143     /**
31144      * 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.
31145      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31146      * @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)
31147      * @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)
31148      * @return {Roo.UpdateManager} The UpdateManager
31149      */
31150     setUrl : function(url, params, loadOnce){
31151         if(this.refreshDelegate){
31152             this.removeListener("activate", this.refreshDelegate);
31153         }
31154         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31155         this.on("activate", this.refreshDelegate);
31156         return this.el.getUpdateManager();
31157     },
31158     
31159     _handleRefresh : function(url, params, loadOnce){
31160         if(!loadOnce || !this.loaded){
31161             var updater = this.el.getUpdateManager();
31162             updater.update(url, params, this._setLoaded.createDelegate(this));
31163         }
31164     },
31165     
31166     _setLoaded : function(){
31167         this.loaded = true;
31168     }, 
31169     
31170     /**
31171      * Returns this panel's id
31172      * @return {String} 
31173      */
31174     getId : function(){
31175         return this.el.id;
31176     },
31177     
31178     /** 
31179      * Returns this panel's element - used by regiosn to add.
31180      * @return {Roo.Element} 
31181      */
31182     getEl : function(){
31183         return this.wrapEl || this.el;
31184     },
31185     
31186     adjustForComponents : function(width, height){
31187         if(this.resizeEl != this.el){
31188             width -= this.el.getFrameWidth('lr');
31189             height -= this.el.getFrameWidth('tb');
31190         }
31191         if(this.toolbar){
31192             var te = this.toolbar.getEl();
31193             height -= te.getHeight();
31194             te.setWidth(width);
31195         }
31196         if(this.adjustments){
31197             width += this.adjustments[0];
31198             height += this.adjustments[1];
31199         }
31200         return {"width": width, "height": height};
31201     },
31202     
31203     setSize : function(width, height){
31204         if(this.fitToFrame && !this.ignoreResize(width, height)){
31205             if(this.fitContainer && this.resizeEl != this.el){
31206                 this.el.setSize(width, height);
31207             }
31208             var size = this.adjustForComponents(width, height);
31209             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31210             this.fireEvent('resize', this, size.width, size.height);
31211         }
31212     },
31213     
31214     /**
31215      * Returns this panel's title
31216      * @return {String} 
31217      */
31218     getTitle : function(){
31219         return this.title;
31220     },
31221     
31222     /**
31223      * Set this panel's title
31224      * @param {String} title
31225      */
31226     setTitle : function(title){
31227         this.title = title;
31228         if(this.region){
31229             this.region.updatePanelTitle(this, title);
31230         }
31231     },
31232     
31233     /**
31234      * Returns true is this panel was configured to be closable
31235      * @return {Boolean} 
31236      */
31237     isClosable : function(){
31238         return this.closable;
31239     },
31240     
31241     beforeSlide : function(){
31242         this.el.clip();
31243         this.resizeEl.clip();
31244     },
31245     
31246     afterSlide : function(){
31247         this.el.unclip();
31248         this.resizeEl.unclip();
31249     },
31250     
31251     /**
31252      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31253      *   Will fail silently if the {@link #setUrl} method has not been called.
31254      *   This does not activate the panel, just updates its content.
31255      */
31256     refresh : function(){
31257         if(this.refreshDelegate){
31258            this.loaded = false;
31259            this.refreshDelegate();
31260         }
31261     },
31262     
31263     /**
31264      * Destroys this panel
31265      */
31266     destroy : function(){
31267         this.el.removeAllListeners();
31268         var tempEl = document.createElement("span");
31269         tempEl.appendChild(this.el.dom);
31270         tempEl.innerHTML = "";
31271         this.el.remove();
31272         this.el = null;
31273     },
31274     
31275       /**
31276      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31277      * <pre><code>
31278
31279 layout.addxtype({
31280        xtype : 'Form',
31281        items: [ .... ]
31282    }
31283 );
31284
31285 </code></pre>
31286      * @param {Object} cfg Xtype definition of item to add.
31287      */
31288     
31289     addxtype : function(cfg) {
31290         // add form..
31291         if (cfg.xtype.match(/^Form$/)) {
31292             var el = this.el.createChild();
31293
31294             this.form = new  Roo.form.Form(cfg);
31295             
31296             
31297             if ( this.form.allItems.length) this.form.render(el.dom);
31298             return this.form;
31299         }
31300         if (['View', 'JsonView'].indexOf(cfg.xtype) > -1) {
31301             // views..
31302             cfg.el = this.el.appendChild(document.createElement("div"));
31303             // factory?
31304             var ret = new Roo[cfg.xtype](cfg);
31305             ret.render(false, ''); // render blank..
31306             return ret;
31307             
31308         }
31309         return false;
31310         
31311     }
31312 });
31313
31314 /**
31315  * @class Roo.GridPanel
31316  * @extends Roo.ContentPanel
31317  * @constructor
31318  * Create a new GridPanel.
31319  * @param {Roo.grid.Grid} grid The grid for this panel
31320  * @param {String/Object} config A string to set only the panel's title, or a config object
31321  */
31322 Roo.GridPanel = function(grid, config){
31323     
31324   
31325     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31326         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31327         
31328     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31329     
31330     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31331     
31332     if(this.toolbar){
31333         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31334     }
31335     // xtype created footer. - not sure if will work as we normally have to render first..
31336     if (this.footer && !this.footer.el && this.footer.xtype) {
31337         
31338         this.footer.container = this.grid.getView().getFooterPanel(true);
31339         this.footer.dataSource = this.grid.dataSource;
31340         this.footer = Roo.factory(this.footer, Roo);
31341         
31342     }
31343     
31344     grid.monitorWindowResize = false; // turn off autosizing
31345     grid.autoHeight = false;
31346     grid.autoWidth = false;
31347     this.grid = grid;
31348     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31349 };
31350
31351 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31352     getId : function(){
31353         return this.grid.id;
31354     },
31355     
31356     /**
31357      * Returns the grid for this panel
31358      * @return {Roo.grid.Grid} 
31359      */
31360     getGrid : function(){
31361         return this.grid;    
31362     },
31363     
31364     setSize : function(width, height){
31365         if(!this.ignoreResize(width, height)){
31366             var grid = this.grid;
31367             var size = this.adjustForComponents(width, height);
31368             grid.getGridEl().setSize(size.width, size.height);
31369             grid.autoSize();
31370         }
31371     },
31372     
31373     beforeSlide : function(){
31374         this.grid.getView().scroller.clip();
31375     },
31376     
31377     afterSlide : function(){
31378         this.grid.getView().scroller.unclip();
31379     },
31380     
31381     destroy : function(){
31382         this.grid.destroy();
31383         delete this.grid;
31384         Roo.GridPanel.superclass.destroy.call(this); 
31385     }
31386 });
31387
31388
31389 /**
31390  * @class Roo.NestedLayoutPanel
31391  * @extends Roo.ContentPanel
31392  * @constructor
31393  * Create a new NestedLayoutPanel.
31394  * 
31395  * 
31396  * @param {Roo.BorderLayout} layout The layout for this panel
31397  * @param {String/Object} config A string to set only the title or a config object
31398  */
31399 Roo.NestedLayoutPanel = function(layout, config)
31400 {
31401     // construct with only one argument..
31402     /* FIXME - implement nicer consturctors
31403     if (layout.layout) {
31404         config = layout;
31405         layout = config.layout;
31406         delete config.layout;
31407     }
31408     if (layout.xtype && !layout.getEl) {
31409         // then layout needs constructing..
31410         layout = Roo.factory(layout, Roo);
31411     }
31412     */
31413     
31414     
31415     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31416     
31417     layout.monitorWindowResize = false; // turn off autosizing
31418     this.layout = layout;
31419     this.layout.getEl().addClass("x-layout-nested-layout");
31420     
31421     
31422     
31423     
31424 };
31425
31426 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31427
31428     setSize : function(width, height){
31429         if(!this.ignoreResize(width, height)){
31430             var size = this.adjustForComponents(width, height);
31431             var el = this.layout.getEl();
31432             el.setSize(size.width, size.height);
31433             var touch = el.dom.offsetWidth;
31434             this.layout.layout();
31435             // ie requires a double layout on the first pass
31436             if(Roo.isIE && !this.initialized){
31437                 this.initialized = true;
31438                 this.layout.layout();
31439             }
31440         }
31441     },
31442     
31443     // activate all subpanels if not currently active..
31444     
31445     setActiveState : function(active){
31446         this.active = active;
31447         if(!active){
31448             this.fireEvent("deactivate", this);
31449             return;
31450         }
31451         
31452         this.fireEvent("activate", this);
31453         // not sure if this should happen before or after..
31454         if (!this.layout) {
31455             return; // should not happen..
31456         }
31457         var reg = false;
31458         for (var r in this.layout.regions) {
31459             reg = this.layout.getRegion(r);
31460             if (reg.getActivePanel()) {
31461                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
31462                 reg.setActivePanel(reg.getActivePanel());
31463                 continue;
31464             }
31465             if (!reg.panels.length) {
31466                 continue;
31467             }
31468             reg.showPanel(reg.getPanel(0));
31469         }
31470         
31471         
31472         
31473         
31474     },
31475     
31476     /**
31477      * Returns the nested BorderLayout for this panel
31478      * @return {Roo.BorderLayout} 
31479      */
31480     getLayout : function(){
31481         return this.layout;
31482     },
31483     
31484      /**
31485      * Adds a xtype elements to the layout of the nested panel
31486      * <pre><code>
31487
31488 panel.addxtype({
31489        xtype : 'ContentPanel',
31490        region: 'west',
31491        items: [ .... ]
31492    }
31493 );
31494
31495 panel.addxtype({
31496         xtype : 'NestedLayoutPanel',
31497         region: 'west',
31498         layout: {
31499            center: { },
31500            west: { }   
31501         },
31502         items : [ ... list of content panels or nested layout panels.. ]
31503    }
31504 );
31505 </code></pre>
31506      * @param {Object} cfg Xtype definition of item to add.
31507      */
31508     addxtype : function(cfg) {
31509         return this.layout.addxtype(cfg);
31510     
31511     }
31512 });
31513
31514 Roo.ScrollPanel = function(el, config, content){
31515     config = config || {};
31516     config.fitToFrame = true;
31517     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31518     
31519     this.el.dom.style.overflow = "hidden";
31520     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31521     this.el.removeClass("x-layout-inactive-content");
31522     this.el.on("mousewheel", this.onWheel, this);
31523
31524     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
31525     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
31526     up.unselectable(); down.unselectable();
31527     up.on("click", this.scrollUp, this);
31528     down.on("click", this.scrollDown, this);
31529     up.addClassOnOver("x-scroller-btn-over");
31530     down.addClassOnOver("x-scroller-btn-over");
31531     up.addClassOnClick("x-scroller-btn-click");
31532     down.addClassOnClick("x-scroller-btn-click");
31533     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31534
31535     this.resizeEl = this.el;
31536     this.el = wrap; this.up = up; this.down = down;
31537 };
31538
31539 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31540     increment : 100,
31541     wheelIncrement : 5,
31542     scrollUp : function(){
31543         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31544     },
31545
31546     scrollDown : function(){
31547         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31548     },
31549
31550     afterScroll : function(){
31551         var el = this.resizeEl;
31552         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31553         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31554         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31555     },
31556
31557     setSize : function(){
31558         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31559         this.afterScroll();
31560     },
31561
31562     onWheel : function(e){
31563         var d = e.getWheelDelta();
31564         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31565         this.afterScroll();
31566         e.stopEvent();
31567     },
31568
31569     setContent : function(content, loadScripts){
31570         this.resizeEl.update(content, loadScripts);
31571     }
31572
31573 });
31574
31575
31576
31577
31578
31579
31580
31581
31582
31583 /**
31584  * @class Roo.TreePanel
31585  * @extends Roo.ContentPanel
31586  * @constructor
31587  * Create a new TreePanel. - defaults to fit/scoll contents.
31588  * @param {String/Object} config A string to set only the panel's title, or a config object
31589  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
31590  */
31591 Roo.TreePanel = function(config){
31592     var el = config.el;
31593     var tree = config.tree;
31594     delete config.tree; 
31595     delete config.el; // hopefull!
31596     
31597     // wrapper for IE7 strict & safari scroll issue
31598     
31599     var treeEl = el.createChild();
31600     config.resizeEl = treeEl;
31601     
31602     
31603     
31604     Roo.TreePanel.superclass.constructor.call(this, el, config);
31605  
31606  
31607     this.tree = new Roo.tree.TreePanel(treeEl , tree);
31608     //console.log(tree);
31609     this.on('activate', function()
31610     {
31611         if (this.tree.rendered) {
31612             return;
31613         }
31614         //console.log('render tree');
31615         this.tree.render();
31616     });
31617     
31618     this.on('resize',  function (cp, w, h) {
31619             this.tree.innerCt.setWidth(w);
31620             this.tree.innerCt.setHeight(h);
31621             this.tree.innerCt.setStyle('overflow-y', 'auto');
31622     });
31623
31624         
31625     
31626 };
31627
31628 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
31629     fitToFrame : true,
31630     autoScroll : true
31631 });
31632
31633
31634
31635
31636
31637
31638
31639
31640
31641
31642
31643 /*
31644  * Based on:
31645  * Ext JS Library 1.1.1
31646  * Copyright(c) 2006-2007, Ext JS, LLC.
31647  *
31648  * Originally Released Under LGPL - original licence link has changed is not relivant.
31649  *
31650  * Fork - LGPL
31651  * <script type="text/javascript">
31652  */
31653  
31654
31655 /**
31656  * @class Roo.ReaderLayout
31657  * @extends Roo.BorderLayout
31658  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
31659  * center region containing two nested regions (a top one for a list view and one for item preview below),
31660  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
31661  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
31662  * expedites the setup of the overall layout and regions for this common application style.
31663  * Example:
31664  <pre><code>
31665 var reader = new Roo.ReaderLayout();
31666 var CP = Roo.ContentPanel;  // shortcut for adding
31667
31668 reader.beginUpdate();
31669 reader.add("north", new CP("north", "North"));
31670 reader.add("west", new CP("west", {title: "West"}));
31671 reader.add("east", new CP("east", {title: "East"}));
31672
31673 reader.regions.listView.add(new CP("listView", "List"));
31674 reader.regions.preview.add(new CP("preview", "Preview"));
31675 reader.endUpdate();
31676 </code></pre>
31677 * @constructor
31678 * Create a new ReaderLayout
31679 * @param {Object} config Configuration options
31680 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
31681 * document.body if omitted)
31682 */
31683 Roo.ReaderLayout = function(config, renderTo){
31684     var c = config || {size:{}};
31685     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
31686         north: c.north !== false ? Roo.apply({
31687             split:false,
31688             initialSize: 32,
31689             titlebar: false
31690         }, c.north) : false,
31691         west: c.west !== false ? Roo.apply({
31692             split:true,
31693             initialSize: 200,
31694             minSize: 175,
31695             maxSize: 400,
31696             titlebar: true,
31697             collapsible: true,
31698             animate: true,
31699             margins:{left:5,right:0,bottom:5,top:5},
31700             cmargins:{left:5,right:5,bottom:5,top:5}
31701         }, c.west) : false,
31702         east: c.east !== false ? Roo.apply({
31703             split:true,
31704             initialSize: 200,
31705             minSize: 175,
31706             maxSize: 400,
31707             titlebar: true,
31708             collapsible: true,
31709             animate: true,
31710             margins:{left:0,right:5,bottom:5,top:5},
31711             cmargins:{left:5,right:5,bottom:5,top:5}
31712         }, c.east) : false,
31713         center: Roo.apply({
31714             tabPosition: 'top',
31715             autoScroll:false,
31716             closeOnTab: true,
31717             titlebar:false,
31718             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
31719         }, c.center)
31720     });
31721
31722     this.el.addClass('x-reader');
31723
31724     this.beginUpdate();
31725
31726     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
31727         south: c.preview !== false ? Roo.apply({
31728             split:true,
31729             initialSize: 200,
31730             minSize: 100,
31731             autoScroll:true,
31732             collapsible:true,
31733             titlebar: true,
31734             cmargins:{top:5,left:0, right:0, bottom:0}
31735         }, c.preview) : false,
31736         center: Roo.apply({
31737             autoScroll:false,
31738             titlebar:false,
31739             minHeight:200
31740         }, c.listView)
31741     });
31742     this.add('center', new Roo.NestedLayoutPanel(inner,
31743             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
31744
31745     this.endUpdate();
31746
31747     this.regions.preview = inner.getRegion('south');
31748     this.regions.listView = inner.getRegion('center');
31749 };
31750
31751 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
31752  * Based on:
31753  * Ext JS Library 1.1.1
31754  * Copyright(c) 2006-2007, Ext JS, LLC.
31755  *
31756  * Originally Released Under LGPL - original licence link has changed is not relivant.
31757  *
31758  * Fork - LGPL
31759  * <script type="text/javascript">
31760  */
31761  
31762 /**
31763  * @class Roo.grid.Grid
31764  * @extends Roo.util.Observable
31765  * This class represents the primary interface of a component based grid control.
31766  * <br><br>Usage:<pre><code>
31767  var grid = new Roo.grid.Grid("my-container-id", {
31768      ds: myDataStore,
31769      cm: myColModel,
31770      selModel: mySelectionModel,
31771      autoSizeColumns: true,
31772      monitorWindowResize: false,
31773      trackMouseOver: true
31774  });
31775  // set any options
31776  grid.render();
31777  * </code></pre>
31778  * <b>Common Problems:</b><br/>
31779  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
31780  * element will correct this<br/>
31781  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
31782  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
31783  * are unpredictable.<br/>
31784  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
31785  * grid to calculate dimensions/offsets.<br/>
31786   * @constructor
31787  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
31788  * The container MUST have some type of size defined for the grid to fill. The container will be
31789  * automatically set to position relative if it isn't already.
31790  * @param {Object} config A config object that sets properties on this grid.
31791  */
31792 Roo.grid.Grid = function(container, config){
31793         // initialize the container
31794         this.container = Roo.get(container);
31795         this.container.update("");
31796         this.container.setStyle("overflow", "hidden");
31797     this.container.addClass('x-grid-container');
31798
31799     this.id = this.container.id;
31800
31801     Roo.apply(this, config);
31802     // check and correct shorthanded configs
31803     if(this.ds){
31804         this.dataSource = this.ds;
31805         delete this.ds;
31806     }
31807     if(this.cm){
31808         this.colModel = this.cm;
31809         delete this.cm;
31810     }
31811     if(this.sm){
31812         this.selModel = this.sm;
31813         delete this.sm;
31814     }
31815
31816     if (this.selModel) {
31817         this.selModel = Roo.factory(this.selModel, Roo.grid);
31818         this.sm = this.selModel;
31819         this.sm.xmodule = this.xmodule || false;
31820     }
31821     if (typeof(this.colModel.config) == 'undefined') {
31822         this.colModel = new Roo.grid.ColumnModel(this.colModel);
31823         this.cm = this.colModel;
31824         this.cm.xmodule = this.xmodule || false;
31825     }
31826     if (this.dataSource) {
31827         this.dataSource= Roo.factory(this.dataSource, Roo.data);
31828         this.ds = this.dataSource;
31829         this.ds.xmodule = this.xmodule || false;
31830         
31831     }
31832     
31833     
31834     
31835     if(this.width){
31836         this.container.setWidth(this.width);
31837     }
31838
31839     if(this.height){
31840         this.container.setHeight(this.height);
31841     }
31842     /** @private */
31843         this.addEvents({
31844         // raw events
31845         /**
31846          * @event click
31847          * The raw click event for the entire grid.
31848          * @param {Roo.EventObject} e
31849          */
31850         "click" : true,
31851         /**
31852          * @event dblclick
31853          * The raw dblclick event for the entire grid.
31854          * @param {Roo.EventObject} e
31855          */
31856         "dblclick" : true,
31857         /**
31858          * @event contextmenu
31859          * The raw contextmenu event for the entire grid.
31860          * @param {Roo.EventObject} e
31861          */
31862         "contextmenu" : true,
31863         /**
31864          * @event mousedown
31865          * The raw mousedown event for the entire grid.
31866          * @param {Roo.EventObject} e
31867          */
31868         "mousedown" : true,
31869         /**
31870          * @event mouseup
31871          * The raw mouseup event for the entire grid.
31872          * @param {Roo.EventObject} e
31873          */
31874         "mouseup" : true,
31875         /**
31876          * @event mouseover
31877          * The raw mouseover event for the entire grid.
31878          * @param {Roo.EventObject} e
31879          */
31880         "mouseover" : true,
31881         /**
31882          * @event mouseout
31883          * The raw mouseout event for the entire grid.
31884          * @param {Roo.EventObject} e
31885          */
31886         "mouseout" : true,
31887         /**
31888          * @event keypress
31889          * The raw keypress event for the entire grid.
31890          * @param {Roo.EventObject} e
31891          */
31892         "keypress" : true,
31893         /**
31894          * @event keydown
31895          * The raw keydown event for the entire grid.
31896          * @param {Roo.EventObject} e
31897          */
31898         "keydown" : true,
31899
31900         // custom events
31901
31902         /**
31903          * @event cellclick
31904          * Fires when a cell is clicked
31905          * @param {Grid} this
31906          * @param {Number} rowIndex
31907          * @param {Number} columnIndex
31908          * @param {Roo.EventObject} e
31909          */
31910         "cellclick" : true,
31911         /**
31912          * @event celldblclick
31913          * Fires when a cell is double clicked
31914          * @param {Grid} this
31915          * @param {Number} rowIndex
31916          * @param {Number} columnIndex
31917          * @param {Roo.EventObject} e
31918          */
31919         "celldblclick" : true,
31920         /**
31921          * @event rowclick
31922          * Fires when a row is clicked
31923          * @param {Grid} this
31924          * @param {Number} rowIndex
31925          * @param {Roo.EventObject} e
31926          */
31927         "rowclick" : true,
31928         /**
31929          * @event rowdblclick
31930          * Fires when a row is double clicked
31931          * @param {Grid} this
31932          * @param {Number} rowIndex
31933          * @param {Roo.EventObject} e
31934          */
31935         "rowdblclick" : true,
31936         /**
31937          * @event headerclick
31938          * Fires when a header is clicked
31939          * @param {Grid} this
31940          * @param {Number} columnIndex
31941          * @param {Roo.EventObject} e
31942          */
31943         "headerclick" : true,
31944         /**
31945          * @event headerdblclick
31946          * Fires when a header cell is double clicked
31947          * @param {Grid} this
31948          * @param {Number} columnIndex
31949          * @param {Roo.EventObject} e
31950          */
31951         "headerdblclick" : true,
31952         /**
31953          * @event rowcontextmenu
31954          * Fires when a row is right clicked
31955          * @param {Grid} this
31956          * @param {Number} rowIndex
31957          * @param {Roo.EventObject} e
31958          */
31959         "rowcontextmenu" : true,
31960         /**
31961          * @event cellcontextmenu
31962          * Fires when a cell is right clicked
31963          * @param {Grid} this
31964          * @param {Number} rowIndex
31965          * @param {Number} cellIndex
31966          * @param {Roo.EventObject} e
31967          */
31968          "cellcontextmenu" : true,
31969         /**
31970          * @event headercontextmenu
31971          * Fires when a header is right clicked
31972          * @param {Grid} this
31973          * @param {Number} columnIndex
31974          * @param {Roo.EventObject} e
31975          */
31976         "headercontextmenu" : true,
31977         /**
31978          * @event bodyscroll
31979          * Fires when the body element is scrolled
31980          * @param {Number} scrollLeft
31981          * @param {Number} scrollTop
31982          */
31983         "bodyscroll" : true,
31984         /**
31985          * @event columnresize
31986          * Fires when the user resizes a column
31987          * @param {Number} columnIndex
31988          * @param {Number} newSize
31989          */
31990         "columnresize" : true,
31991         /**
31992          * @event columnmove
31993          * Fires when the user moves a column
31994          * @param {Number} oldIndex
31995          * @param {Number} newIndex
31996          */
31997         "columnmove" : true,
31998         /**
31999          * @event startdrag
32000          * Fires when row(s) start being dragged
32001          * @param {Grid} this
32002          * @param {Roo.GridDD} dd The drag drop object
32003          * @param {event} e The raw browser event
32004          */
32005         "startdrag" : true,
32006         /**
32007          * @event enddrag
32008          * Fires when a drag operation is complete
32009          * @param {Grid} this
32010          * @param {Roo.GridDD} dd The drag drop object
32011          * @param {event} e The raw browser event
32012          */
32013         "enddrag" : true,
32014         /**
32015          * @event dragdrop
32016          * Fires when dragged row(s) are dropped on a valid DD target
32017          * @param {Grid} this
32018          * @param {Roo.GridDD} dd The drag drop object
32019          * @param {String} targetId The target drag drop object
32020          * @param {event} e The raw browser event
32021          */
32022         "dragdrop" : true,
32023         /**
32024          * @event dragover
32025          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
32026          * @param {Grid} this
32027          * @param {Roo.GridDD} dd The drag drop object
32028          * @param {String} targetId The target drag drop object
32029          * @param {event} e The raw browser event
32030          */
32031         "dragover" : true,
32032         /**
32033          * @event dragenter
32034          *  Fires when the dragged row(s) first cross another DD target while being dragged
32035          * @param {Grid} this
32036          * @param {Roo.GridDD} dd The drag drop object
32037          * @param {String} targetId The target drag drop object
32038          * @param {event} e The raw browser event
32039          */
32040         "dragenter" : true,
32041         /**
32042          * @event dragout
32043          * Fires when the dragged row(s) leave another DD target while being dragged
32044          * @param {Grid} this
32045          * @param {Roo.GridDD} dd The drag drop object
32046          * @param {String} targetId The target drag drop object
32047          * @param {event} e The raw browser event
32048          */
32049         "dragout" : true,
32050         /**
32051          * @event rowclass
32052          * Fires when a row is rendered, so you can change add a style to it.
32053          * @param {GridView} gridview   The grid view
32054          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
32055          */
32056         'rowclass' : true,
32057
32058         /**
32059          * @event render
32060          * Fires when the grid is rendered
32061          * @param {Grid} grid
32062          */
32063         'render' : true
32064     });
32065
32066     Roo.grid.Grid.superclass.constructor.call(this);
32067 };
32068 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
32069     
32070     /**
32071      * @cfg {String} ddGroup - drag drop group.
32072      */
32073
32074     /**
32075      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32076      */
32077     minColumnWidth : 25,
32078
32079     /**
32080      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32081      * <b>on initial render.</b> It is more efficient to explicitly size the columns
32082      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
32083      */
32084     autoSizeColumns : false,
32085
32086     /**
32087      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32088      */
32089     autoSizeHeaders : true,
32090
32091     /**
32092      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32093      */
32094     monitorWindowResize : true,
32095
32096     /**
32097      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32098      * rows measured to get a columns size. Default is 0 (all rows).
32099      */
32100     maxRowsToMeasure : 0,
32101
32102     /**
32103      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32104      */
32105     trackMouseOver : true,
32106
32107     /**
32108     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
32109     */
32110     
32111     /**
32112     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32113     */
32114     enableDragDrop : false,
32115     
32116     /**
32117     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32118     */
32119     enableColumnMove : true,
32120     
32121     /**
32122     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32123     */
32124     enableColumnHide : true,
32125     
32126     /**
32127     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32128     */
32129     enableRowHeightSync : false,
32130     
32131     /**
32132     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32133     */
32134     stripeRows : true,
32135     
32136     /**
32137     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32138     */
32139     autoHeight : false,
32140
32141     /**
32142      * @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.
32143      */
32144     autoExpandColumn : false,
32145
32146     /**
32147     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32148     * Default is 50.
32149     */
32150     autoExpandMin : 50,
32151
32152     /**
32153     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32154     */
32155     autoExpandMax : 1000,
32156
32157     /**
32158     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32159     */
32160     view : null,
32161
32162     /**
32163     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32164     */
32165     loadMask : false,
32166     /**
32167     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
32168     */
32169     dropTarget: false,
32170     
32171     // private
32172     rendered : false,
32173
32174     /**
32175     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32176     * of a fixed width. Default is false.
32177     */
32178     /**
32179     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32180     */
32181     /**
32182      * Called once after all setup has been completed and the grid is ready to be rendered.
32183      * @return {Roo.grid.Grid} this
32184      */
32185     render : function()
32186     {
32187         var c = this.container;
32188         // try to detect autoHeight/width mode
32189         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32190             this.autoHeight = true;
32191         }
32192         var view = this.getView();
32193         view.init(this);
32194
32195         c.on("click", this.onClick, this);
32196         c.on("dblclick", this.onDblClick, this);
32197         c.on("contextmenu", this.onContextMenu, this);
32198         c.on("keydown", this.onKeyDown, this);
32199
32200         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32201
32202         this.getSelectionModel().init(this);
32203
32204         view.render();
32205
32206         if(this.loadMask){
32207             this.loadMask = new Roo.LoadMask(this.container,
32208                     Roo.apply({store:this.dataSource}, this.loadMask));
32209         }
32210         
32211         
32212         if (this.toolbar && this.toolbar.xtype) {
32213             this.toolbar.container = this.getView().getHeaderPanel(true);
32214             this.toolbar = new Ext.Toolbar(this.toolbar);
32215         }
32216         if (this.footer && this.footer.xtype) {
32217             this.footer.dataSource = this.getDataSource();
32218             this.footer.container = this.getView().getFooterPanel(true);
32219             this.footer = Roo.factory(this.footer, Roo);
32220         }
32221         if (this.dropTarget && this.dropTarget.xtype) {
32222             delete this.dropTarget.xtype;
32223             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32224         }
32225         
32226         
32227         this.rendered = true;
32228         this.fireEvent('render', this);
32229         return this;
32230     },
32231
32232         /**
32233          * Reconfigures the grid to use a different Store and Column Model.
32234          * The View will be bound to the new objects and refreshed.
32235          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32236          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32237          */
32238     reconfigure : function(dataSource, colModel){
32239         if(this.loadMask){
32240             this.loadMask.destroy();
32241             this.loadMask = new Roo.LoadMask(this.container,
32242                     Roo.apply({store:dataSource}, this.loadMask));
32243         }
32244         this.view.bind(dataSource, colModel);
32245         this.dataSource = dataSource;
32246         this.colModel = colModel;
32247         this.view.refresh(true);
32248     },
32249
32250     // private
32251     onKeyDown : function(e){
32252         this.fireEvent("keydown", e);
32253     },
32254
32255     /**
32256      * Destroy this grid.
32257      * @param {Boolean} removeEl True to remove the element
32258      */
32259     destroy : function(removeEl, keepListeners){
32260         if(this.loadMask){
32261             this.loadMask.destroy();
32262         }
32263         var c = this.container;
32264         c.removeAllListeners();
32265         this.view.destroy();
32266         this.colModel.purgeListeners();
32267         if(!keepListeners){
32268             this.purgeListeners();
32269         }
32270         c.update("");
32271         if(removeEl === true){
32272             c.remove();
32273         }
32274     },
32275
32276     // private
32277     processEvent : function(name, e){
32278         this.fireEvent(name, e);
32279         var t = e.getTarget();
32280         var v = this.view;
32281         var header = v.findHeaderIndex(t);
32282         if(header !== false){
32283             this.fireEvent("header" + name, this, header, e);
32284         }else{
32285             var row = v.findRowIndex(t);
32286             var cell = v.findCellIndex(t);
32287             if(row !== false){
32288                 this.fireEvent("row" + name, this, row, e);
32289                 if(cell !== false){
32290                     this.fireEvent("cell" + name, this, row, cell, e);
32291                 }
32292             }
32293         }
32294     },
32295
32296     // private
32297     onClick : function(e){
32298         this.processEvent("click", e);
32299     },
32300
32301     // private
32302     onContextMenu : function(e, t){
32303         this.processEvent("contextmenu", e);
32304     },
32305
32306     // private
32307     onDblClick : function(e){
32308         this.processEvent("dblclick", e);
32309     },
32310
32311     // private
32312     walkCells : function(row, col, step, fn, scope){
32313         var cm = this.colModel, clen = cm.getColumnCount();
32314         var ds = this.dataSource, rlen = ds.getCount(), first = true;
32315         if(step < 0){
32316             if(col < 0){
32317                 row--;
32318                 first = false;
32319             }
32320             while(row >= 0){
32321                 if(!first){
32322                     col = clen-1;
32323                 }
32324                 first = false;
32325                 while(col >= 0){
32326                     if(fn.call(scope || this, row, col, cm) === true){
32327                         return [row, col];
32328                     }
32329                     col--;
32330                 }
32331                 row--;
32332             }
32333         } else {
32334             if(col >= clen){
32335                 row++;
32336                 first = false;
32337             }
32338             while(row < rlen){
32339                 if(!first){
32340                     col = 0;
32341                 }
32342                 first = false;
32343                 while(col < clen){
32344                     if(fn.call(scope || this, row, col, cm) === true){
32345                         return [row, col];
32346                     }
32347                     col++;
32348                 }
32349                 row++;
32350             }
32351         }
32352         return null;
32353     },
32354
32355     // private
32356     getSelections : function(){
32357         return this.selModel.getSelections();
32358     },
32359
32360     /**
32361      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32362      * but if manual update is required this method will initiate it.
32363      */
32364     autoSize : function(){
32365         if(this.rendered){
32366             this.view.layout();
32367             if(this.view.adjustForScroll){
32368                 this.view.adjustForScroll();
32369             }
32370         }
32371     },
32372
32373     /**
32374      * Returns the grid's underlying element.
32375      * @return {Element} The element
32376      */
32377     getGridEl : function(){
32378         return this.container;
32379     },
32380
32381     // private for compatibility, overridden by editor grid
32382     stopEditing : function(){},
32383
32384     /**
32385      * Returns the grid's SelectionModel.
32386      * @return {SelectionModel}
32387      */
32388     getSelectionModel : function(){
32389         if(!this.selModel){
32390             this.selModel = new Roo.grid.RowSelectionModel();
32391         }
32392         return this.selModel;
32393     },
32394
32395     /**
32396      * Returns the grid's DataSource.
32397      * @return {DataSource}
32398      */
32399     getDataSource : function(){
32400         return this.dataSource;
32401     },
32402
32403     /**
32404      * Returns the grid's ColumnModel.
32405      * @return {ColumnModel}
32406      */
32407     getColumnModel : function(){
32408         return this.colModel;
32409     },
32410
32411     /**
32412      * Returns the grid's GridView object.
32413      * @return {GridView}
32414      */
32415     getView : function(){
32416         if(!this.view){
32417             this.view = new Roo.grid.GridView(this.viewConfig);
32418         }
32419         return this.view;
32420     },
32421     /**
32422      * Called to get grid's drag proxy text, by default returns this.ddText.
32423      * @return {String}
32424      */
32425     getDragDropText : function(){
32426         var count = this.selModel.getCount();
32427         return String.format(this.ddText, count, count == 1 ? '' : 's');
32428     }
32429 });
32430 /**
32431  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32432  * %0 is replaced with the number of selected rows.
32433  * @type String
32434  */
32435 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
32436  * Based on:
32437  * Ext JS Library 1.1.1
32438  * Copyright(c) 2006-2007, Ext JS, LLC.
32439  *
32440  * Originally Released Under LGPL - original licence link has changed is not relivant.
32441  *
32442  * Fork - LGPL
32443  * <script type="text/javascript">
32444  */
32445  
32446 Roo.grid.AbstractGridView = function(){
32447         this.grid = null;
32448         
32449         this.events = {
32450             "beforerowremoved" : true,
32451             "beforerowsinserted" : true,
32452             "beforerefresh" : true,
32453             "rowremoved" : true,
32454             "rowsinserted" : true,
32455             "rowupdated" : true,
32456             "refresh" : true
32457         };
32458     Roo.grid.AbstractGridView.superclass.constructor.call(this);
32459 };
32460
32461 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32462     rowClass : "x-grid-row",
32463     cellClass : "x-grid-cell",
32464     tdClass : "x-grid-td",
32465     hdClass : "x-grid-hd",
32466     splitClass : "x-grid-hd-split",
32467     
32468         init: function(grid){
32469         this.grid = grid;
32470                 var cid = this.grid.getGridEl().id;
32471         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32472         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32473         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32474         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
32475         },
32476         
32477         getColumnRenderers : function(){
32478         var renderers = [];
32479         var cm = this.grid.colModel;
32480         var colCount = cm.getColumnCount();
32481         for(var i = 0; i < colCount; i++){
32482             renderers[i] = cm.getRenderer(i);
32483         }
32484         return renderers;
32485     },
32486     
32487     getColumnIds : function(){
32488         var ids = [];
32489         var cm = this.grid.colModel;
32490         var colCount = cm.getColumnCount();
32491         for(var i = 0; i < colCount; i++){
32492             ids[i] = cm.getColumnId(i);
32493         }
32494         return ids;
32495     },
32496     
32497     getDataIndexes : function(){
32498         if(!this.indexMap){
32499             this.indexMap = this.buildIndexMap();
32500         }
32501         return this.indexMap.colToData;
32502     },
32503     
32504     getColumnIndexByDataIndex : function(dataIndex){
32505         if(!this.indexMap){
32506             this.indexMap = this.buildIndexMap();
32507         }
32508         return this.indexMap.dataToCol[dataIndex];
32509     },
32510     
32511     /**
32512      * Set a css style for a column dynamically. 
32513      * @param {Number} colIndex The index of the column
32514      * @param {String} name The css property name
32515      * @param {String} value The css value
32516      */
32517     setCSSStyle : function(colIndex, name, value){
32518         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
32519         Roo.util.CSS.updateRule(selector, name, value);
32520     },
32521     
32522     generateRules : function(cm){
32523         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
32524         Roo.util.CSS.removeStyleSheet(rulesId);
32525         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32526             var cid = cm.getColumnId(i);
32527             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
32528                          this.tdSelector, cid, " {\n}\n",
32529                          this.hdSelector, cid, " {\n}\n",
32530                          this.splitSelector, cid, " {\n}\n");
32531         }
32532         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
32533     }
32534 });/*
32535  * Based on:
32536  * Ext JS Library 1.1.1
32537  * Copyright(c) 2006-2007, Ext JS, LLC.
32538  *
32539  * Originally Released Under LGPL - original licence link has changed is not relivant.
32540  *
32541  * Fork - LGPL
32542  * <script type="text/javascript">
32543  */
32544
32545 // private
32546 // This is a support class used internally by the Grid components
32547 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
32548     this.grid = grid;
32549     this.view = grid.getView();
32550     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32551     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
32552     if(hd2){
32553         this.setHandleElId(Roo.id(hd));
32554         this.setOuterHandleElId(Roo.id(hd2));
32555     }
32556     this.scroll = false;
32557 };
32558 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
32559     maxDragWidth: 120,
32560     getDragData : function(e){
32561         var t = Roo.lib.Event.getTarget(e);
32562         var h = this.view.findHeaderCell(t);
32563         if(h){
32564             return {ddel: h.firstChild, header:h};
32565         }
32566         return false;
32567     },
32568
32569     onInitDrag : function(e){
32570         this.view.headersDisabled = true;
32571         var clone = this.dragData.ddel.cloneNode(true);
32572         clone.id = Roo.id();
32573         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
32574         this.proxy.update(clone);
32575         return true;
32576     },
32577
32578     afterValidDrop : function(){
32579         var v = this.view;
32580         setTimeout(function(){
32581             v.headersDisabled = false;
32582         }, 50);
32583     },
32584
32585     afterInvalidDrop : function(){
32586         var v = this.view;
32587         setTimeout(function(){
32588             v.headersDisabled = false;
32589         }, 50);
32590     }
32591 });
32592 /*
32593  * Based on:
32594  * Ext JS Library 1.1.1
32595  * Copyright(c) 2006-2007, Ext JS, LLC.
32596  *
32597  * Originally Released Under LGPL - original licence link has changed is not relivant.
32598  *
32599  * Fork - LGPL
32600  * <script type="text/javascript">
32601  */
32602 // private
32603 // This is a support class used internally by the Grid components
32604 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
32605     this.grid = grid;
32606     this.view = grid.getView();
32607     // split the proxies so they don't interfere with mouse events
32608     this.proxyTop = Roo.DomHelper.append(document.body, {
32609         cls:"col-move-top", html:"&#160;"
32610     }, true);
32611     this.proxyBottom = Roo.DomHelper.append(document.body, {
32612         cls:"col-move-bottom", html:"&#160;"
32613     }, true);
32614     this.proxyTop.hide = this.proxyBottom.hide = function(){
32615         this.setLeftTop(-100,-100);
32616         this.setStyle("visibility", "hidden");
32617     };
32618     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32619     // temporarily disabled
32620     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
32621     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
32622 };
32623 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
32624     proxyOffsets : [-4, -9],
32625     fly: Roo.Element.fly,
32626
32627     getTargetFromEvent : function(e){
32628         var t = Roo.lib.Event.getTarget(e);
32629         var cindex = this.view.findCellIndex(t);
32630         if(cindex !== false){
32631             return this.view.getHeaderCell(cindex);
32632         }
32633     },
32634
32635     nextVisible : function(h){
32636         var v = this.view, cm = this.grid.colModel;
32637         h = h.nextSibling;
32638         while(h){
32639             if(!cm.isHidden(v.getCellIndex(h))){
32640                 return h;
32641             }
32642             h = h.nextSibling;
32643         }
32644         return null;
32645     },
32646
32647     prevVisible : function(h){
32648         var v = this.view, cm = this.grid.colModel;
32649         h = h.prevSibling;
32650         while(h){
32651             if(!cm.isHidden(v.getCellIndex(h))){
32652                 return h;
32653             }
32654             h = h.prevSibling;
32655         }
32656         return null;
32657     },
32658
32659     positionIndicator : function(h, n, e){
32660         var x = Roo.lib.Event.getPageX(e);
32661         var r = Roo.lib.Dom.getRegion(n.firstChild);
32662         var px, pt, py = r.top + this.proxyOffsets[1];
32663         if((r.right - x) <= (r.right-r.left)/2){
32664             px = r.right+this.view.borderWidth;
32665             pt = "after";
32666         }else{
32667             px = r.left;
32668             pt = "before";
32669         }
32670         var oldIndex = this.view.getCellIndex(h);
32671         var newIndex = this.view.getCellIndex(n);
32672
32673         if(this.grid.colModel.isFixed(newIndex)){
32674             return false;
32675         }
32676
32677         var locked = this.grid.colModel.isLocked(newIndex);
32678
32679         if(pt == "after"){
32680             newIndex++;
32681         }
32682         if(oldIndex < newIndex){
32683             newIndex--;
32684         }
32685         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
32686             return false;
32687         }
32688         px +=  this.proxyOffsets[0];
32689         this.proxyTop.setLeftTop(px, py);
32690         this.proxyTop.show();
32691         if(!this.bottomOffset){
32692             this.bottomOffset = this.view.mainHd.getHeight();
32693         }
32694         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
32695         this.proxyBottom.show();
32696         return pt;
32697     },
32698
32699     onNodeEnter : function(n, dd, e, data){
32700         if(data.header != n){
32701             this.positionIndicator(data.header, n, e);
32702         }
32703     },
32704
32705     onNodeOver : function(n, dd, e, data){
32706         var result = false;
32707         if(data.header != n){
32708             result = this.positionIndicator(data.header, n, e);
32709         }
32710         if(!result){
32711             this.proxyTop.hide();
32712             this.proxyBottom.hide();
32713         }
32714         return result ? this.dropAllowed : this.dropNotAllowed;
32715     },
32716
32717     onNodeOut : function(n, dd, e, data){
32718         this.proxyTop.hide();
32719         this.proxyBottom.hide();
32720     },
32721
32722     onNodeDrop : function(n, dd, e, data){
32723         var h = data.header;
32724         if(h != n){
32725             var cm = this.grid.colModel;
32726             var x = Roo.lib.Event.getPageX(e);
32727             var r = Roo.lib.Dom.getRegion(n.firstChild);
32728             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
32729             var oldIndex = this.view.getCellIndex(h);
32730             var newIndex = this.view.getCellIndex(n);
32731             var locked = cm.isLocked(newIndex);
32732             if(pt == "after"){
32733                 newIndex++;
32734             }
32735             if(oldIndex < newIndex){
32736                 newIndex--;
32737             }
32738             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
32739                 return false;
32740             }
32741             cm.setLocked(oldIndex, locked, true);
32742             cm.moveColumn(oldIndex, newIndex);
32743             this.grid.fireEvent("columnmove", oldIndex, newIndex);
32744             return true;
32745         }
32746         return false;
32747     }
32748 });
32749 /*
32750  * Based on:
32751  * Ext JS Library 1.1.1
32752  * Copyright(c) 2006-2007, Ext JS, LLC.
32753  *
32754  * Originally Released Under LGPL - original licence link has changed is not relivant.
32755  *
32756  * Fork - LGPL
32757  * <script type="text/javascript">
32758  */
32759   
32760 /**
32761  * @class Roo.grid.GridView
32762  * @extends Roo.util.Observable
32763  *
32764  * @constructor
32765  * @param {Object} config
32766  */
32767 Roo.grid.GridView = function(config){
32768     Roo.grid.GridView.superclass.constructor.call(this);
32769     this.el = null;
32770
32771     Roo.apply(this, config);
32772 };
32773
32774 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
32775
32776     /**
32777      * Override this function to apply custom css classes to rows during rendering
32778      * @param {Record} record The record
32779      * @param {Number} index
32780      * @method getRowClass
32781      */
32782     rowClass : "x-grid-row",
32783
32784     cellClass : "x-grid-col",
32785
32786     tdClass : "x-grid-td",
32787
32788     hdClass : "x-grid-hd",
32789
32790     splitClass : "x-grid-split",
32791
32792     sortClasses : ["sort-asc", "sort-desc"],
32793
32794     enableMoveAnim : false,
32795
32796     hlColor: "C3DAF9",
32797
32798     dh : Roo.DomHelper,
32799
32800     fly : Roo.Element.fly,
32801
32802     css : Roo.util.CSS,
32803
32804     borderWidth: 1,
32805
32806     splitOffset: 3,
32807
32808     scrollIncrement : 22,
32809
32810     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
32811
32812     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
32813
32814     bind : function(ds, cm){
32815         if(this.ds){
32816             this.ds.un("load", this.onLoad, this);
32817             this.ds.un("datachanged", this.onDataChange, this);
32818             this.ds.un("add", this.onAdd, this);
32819             this.ds.un("remove", this.onRemove, this);
32820             this.ds.un("update", this.onUpdate, this);
32821             this.ds.un("clear", this.onClear, this);
32822         }
32823         if(ds){
32824             ds.on("load", this.onLoad, this);
32825             ds.on("datachanged", this.onDataChange, this);
32826             ds.on("add", this.onAdd, this);
32827             ds.on("remove", this.onRemove, this);
32828             ds.on("update", this.onUpdate, this);
32829             ds.on("clear", this.onClear, this);
32830         }
32831         this.ds = ds;
32832
32833         if(this.cm){
32834             this.cm.un("widthchange", this.onColWidthChange, this);
32835             this.cm.un("headerchange", this.onHeaderChange, this);
32836             this.cm.un("hiddenchange", this.onHiddenChange, this);
32837             this.cm.un("columnmoved", this.onColumnMove, this);
32838             this.cm.un("columnlockchange", this.onColumnLock, this);
32839         }
32840         if(cm){
32841             this.generateRules(cm);
32842             cm.on("widthchange", this.onColWidthChange, this);
32843             cm.on("headerchange", this.onHeaderChange, this);
32844             cm.on("hiddenchange", this.onHiddenChange, this);
32845             cm.on("columnmoved", this.onColumnMove, this);
32846             cm.on("columnlockchange", this.onColumnLock, this);
32847         }
32848         this.cm = cm;
32849     },
32850
32851     init: function(grid){
32852         Roo.grid.GridView.superclass.init.call(this, grid);
32853
32854         this.bind(grid.dataSource, grid.colModel);
32855
32856         grid.on("headerclick", this.handleHeaderClick, this);
32857
32858         if(grid.trackMouseOver){
32859             grid.on("mouseover", this.onRowOver, this);
32860             grid.on("mouseout", this.onRowOut, this);
32861         }
32862         grid.cancelTextSelection = function(){};
32863         this.gridId = grid.id;
32864
32865         var tpls = this.templates || {};
32866
32867         if(!tpls.master){
32868             tpls.master = new Roo.Template(
32869                '<div class="x-grid" hidefocus="true">',
32870                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
32871                   '<div class="x-grid-topbar"></div>',
32872                   '<div class="x-grid-scroller"><div></div></div>',
32873                   '<div class="x-grid-locked">',
32874                       '<div class="x-grid-header">{lockedHeader}</div>',
32875                       '<div class="x-grid-body">{lockedBody}</div>',
32876                   "</div>",
32877                   '<div class="x-grid-viewport">',
32878                       '<div class="x-grid-header">{header}</div>',
32879                       '<div class="x-grid-body">{body}</div>',
32880                   "</div>",
32881                   '<div class="x-grid-bottombar"></div>',
32882                  
32883                   '<div class="x-grid-resize-proxy">&#160;</div>',
32884                "</div>"
32885             );
32886             tpls.master.disableformats = true;
32887         }
32888
32889         if(!tpls.header){
32890             tpls.header = new Roo.Template(
32891                '<table border="0" cellspacing="0" cellpadding="0">',
32892                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
32893                "</table>{splits}"
32894             );
32895             tpls.header.disableformats = true;
32896         }
32897         tpls.header.compile();
32898
32899         if(!tpls.hcell){
32900             tpls.hcell = new Roo.Template(
32901                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
32902                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
32903                 "</div></td>"
32904              );
32905              tpls.hcell.disableFormats = true;
32906         }
32907         tpls.hcell.compile();
32908
32909         if(!tpls.hsplit){
32910             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
32911             tpls.hsplit.disableFormats = true;
32912         }
32913         tpls.hsplit.compile();
32914
32915         if(!tpls.body){
32916             tpls.body = new Roo.Template(
32917                '<table border="0" cellspacing="0" cellpadding="0">',
32918                "<tbody>{rows}</tbody>",
32919                "</table>"
32920             );
32921             tpls.body.disableFormats = true;
32922         }
32923         tpls.body.compile();
32924
32925         if(!tpls.row){
32926             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
32927             tpls.row.disableFormats = true;
32928         }
32929         tpls.row.compile();
32930
32931         if(!tpls.cell){
32932             tpls.cell = new Roo.Template(
32933                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
32934                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
32935                 "</td>"
32936             );
32937             tpls.cell.disableFormats = true;
32938         }
32939         tpls.cell.compile();
32940
32941         this.templates = tpls;
32942     },
32943
32944     // remap these for backwards compat
32945     onColWidthChange : function(){
32946         this.updateColumns.apply(this, arguments);
32947     },
32948     onHeaderChange : function(){
32949         this.updateHeaders.apply(this, arguments);
32950     }, 
32951     onHiddenChange : function(){
32952         this.handleHiddenChange.apply(this, arguments);
32953     },
32954     onColumnMove : function(){
32955         this.handleColumnMove.apply(this, arguments);
32956     },
32957     onColumnLock : function(){
32958         this.handleLockChange.apply(this, arguments);
32959     },
32960
32961     onDataChange : function(){
32962         this.refresh();
32963         this.updateHeaderSortState();
32964     },
32965
32966     onClear : function(){
32967         this.refresh();
32968     },
32969
32970     onUpdate : function(ds, record){
32971         this.refreshRow(record);
32972     },
32973
32974     refreshRow : function(record){
32975         var ds = this.ds, index;
32976         if(typeof record == 'number'){
32977             index = record;
32978             record = ds.getAt(index);
32979         }else{
32980             index = ds.indexOf(record);
32981         }
32982         this.insertRows(ds, index, index, true);
32983         this.onRemove(ds, record, index+1, true);
32984         this.syncRowHeights(index, index);
32985         this.layout();
32986         this.fireEvent("rowupdated", this, index, record);
32987     },
32988
32989     onAdd : function(ds, records, index){
32990         this.insertRows(ds, index, index + (records.length-1));
32991     },
32992
32993     onRemove : function(ds, record, index, isUpdate){
32994         if(isUpdate !== true){
32995             this.fireEvent("beforerowremoved", this, index, record);
32996         }
32997         var bt = this.getBodyTable(), lt = this.getLockedTable();
32998         if(bt.rows[index]){
32999             bt.firstChild.removeChild(bt.rows[index]);
33000         }
33001         if(lt.rows[index]){
33002             lt.firstChild.removeChild(lt.rows[index]);
33003         }
33004         if(isUpdate !== true){
33005             this.stripeRows(index);
33006             this.syncRowHeights(index, index);
33007             this.layout();
33008             this.fireEvent("rowremoved", this, index, record);
33009         }
33010     },
33011
33012     onLoad : function(){
33013         this.scrollToTop();
33014     },
33015
33016     /**
33017      * Scrolls the grid to the top
33018      */
33019     scrollToTop : function(){
33020         if(this.scroller){
33021             this.scroller.dom.scrollTop = 0;
33022             this.syncScroll();
33023         }
33024     },
33025
33026     /**
33027      * Gets a panel in the header of the grid that can be used for toolbars etc.
33028      * After modifying the contents of this panel a call to grid.autoSize() may be
33029      * required to register any changes in size.
33030      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
33031      * @return Roo.Element
33032      */
33033     getHeaderPanel : function(doShow){
33034         if(doShow){
33035             this.headerPanel.show();
33036         }
33037         return this.headerPanel;
33038     },
33039
33040     /**
33041      * Gets a panel in the footer of the grid that can be used for toolbars etc.
33042      * After modifying the contents of this panel a call to grid.autoSize() may be
33043      * required to register any changes in size.
33044      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
33045      * @return Roo.Element
33046      */
33047     getFooterPanel : function(doShow){
33048         if(doShow){
33049             this.footerPanel.show();
33050         }
33051         return this.footerPanel;
33052     },
33053
33054     initElements : function(){
33055         var E = Roo.Element;
33056         var el = this.grid.getGridEl().dom.firstChild;
33057         var cs = el.childNodes;
33058
33059         this.el = new E(el);
33060         
33061          this.focusEl = new E(el.firstChild);
33062         this.focusEl.swallowEvent("click", true);
33063         
33064         this.headerPanel = new E(cs[1]);
33065         this.headerPanel.enableDisplayMode("block");
33066
33067         this.scroller = new E(cs[2]);
33068         this.scrollSizer = new E(this.scroller.dom.firstChild);
33069
33070         this.lockedWrap = new E(cs[3]);
33071         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33072         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33073
33074         this.mainWrap = new E(cs[4]);
33075         this.mainHd = new E(this.mainWrap.dom.firstChild);
33076         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33077
33078         this.footerPanel = new E(cs[5]);
33079         this.footerPanel.enableDisplayMode("block");
33080
33081         this.resizeProxy = new E(cs[6]);
33082
33083         this.headerSelector = String.format(
33084            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33085            this.lockedHd.id, this.mainHd.id
33086         );
33087
33088         this.splitterSelector = String.format(
33089            '#{0} div.x-grid-split, #{1} div.x-grid-split',
33090            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33091         );
33092     },
33093     idToCssName : function(s)
33094     {
33095         return s.replace(/[^a-z0-9]+/ig, '-');
33096     },
33097
33098     getHeaderCell : function(index){
33099         return Roo.DomQuery.select(this.headerSelector)[index];
33100     },
33101
33102     getHeaderCellMeasure : function(index){
33103         return this.getHeaderCell(index).firstChild;
33104     },
33105
33106     getHeaderCellText : function(index){
33107         return this.getHeaderCell(index).firstChild.firstChild;
33108     },
33109
33110     getLockedTable : function(){
33111         return this.lockedBody.dom.firstChild;
33112     },
33113
33114     getBodyTable : function(){
33115         return this.mainBody.dom.firstChild;
33116     },
33117
33118     getLockedRow : function(index){
33119         return this.getLockedTable().rows[index];
33120     },
33121
33122     getRow : function(index){
33123         return this.getBodyTable().rows[index];
33124     },
33125
33126     getRowComposite : function(index){
33127         if(!this.rowEl){
33128             this.rowEl = new Roo.CompositeElementLite();
33129         }
33130         var els = [], lrow, mrow;
33131         if(lrow = this.getLockedRow(index)){
33132             els.push(lrow);
33133         }
33134         if(mrow = this.getRow(index)){
33135             els.push(mrow);
33136         }
33137         this.rowEl.elements = els;
33138         return this.rowEl;
33139     },
33140
33141     getCell : function(rowIndex, colIndex){
33142         var locked = this.cm.getLockedCount();
33143         var source;
33144         if(colIndex < locked){
33145             source = this.lockedBody.dom.firstChild;
33146         }else{
33147             source = this.mainBody.dom.firstChild;
33148             colIndex -= locked;
33149         }
33150         return source.rows[rowIndex].childNodes[colIndex];
33151     },
33152
33153     getCellText : function(rowIndex, colIndex){
33154         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33155     },
33156
33157     getCellBox : function(cell){
33158         var b = this.fly(cell).getBox();
33159         if(Roo.isOpera){ // opera fails to report the Y
33160             b.y = cell.offsetTop + this.mainBody.getY();
33161         }
33162         return b;
33163     },
33164
33165     getCellIndex : function(cell){
33166         var id = String(cell.className).match(this.cellRE);
33167         if(id){
33168             return parseInt(id[1], 10);
33169         }
33170         return 0;
33171     },
33172
33173     findHeaderIndex : function(n){
33174         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33175         return r ? this.getCellIndex(r) : false;
33176     },
33177
33178     findHeaderCell : function(n){
33179         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33180         return r ? r : false;
33181     },
33182
33183     findRowIndex : function(n){
33184         if(!n){
33185             return false;
33186         }
33187         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33188         return r ? r.rowIndex : false;
33189     },
33190
33191     findCellIndex : function(node){
33192         var stop = this.el.dom;
33193         while(node && node != stop){
33194             if(this.findRE.test(node.className)){
33195                 return this.getCellIndex(node);
33196             }
33197             node = node.parentNode;
33198         }
33199         return false;
33200     },
33201
33202     getColumnId : function(index){
33203         return this.cm.getColumnId(index);
33204     },
33205
33206     getSplitters : function()
33207     {
33208         if(this.splitterSelector){
33209            return Roo.DomQuery.select(this.splitterSelector);
33210         }else{
33211             return null;
33212       }
33213     },
33214
33215     getSplitter : function(index){
33216         return this.getSplitters()[index];
33217     },
33218
33219     onRowOver : function(e, t){
33220         var row;
33221         if((row = this.findRowIndex(t)) !== false){
33222             this.getRowComposite(row).addClass("x-grid-row-over");
33223         }
33224     },
33225
33226     onRowOut : function(e, t){
33227         var row;
33228         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33229             this.getRowComposite(row).removeClass("x-grid-row-over");
33230         }
33231     },
33232
33233     renderHeaders : function(){
33234         var cm = this.cm;
33235         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33236         var cb = [], lb = [], sb = [], lsb = [], p = {};
33237         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33238             p.cellId = "x-grid-hd-0-" + i;
33239             p.splitId = "x-grid-csplit-0-" + i;
33240             p.id = cm.getColumnId(i);
33241             p.title = cm.getColumnTooltip(i) || "";
33242             p.value = cm.getColumnHeader(i) || "";
33243             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33244             if(!cm.isLocked(i)){
33245                 cb[cb.length] = ct.apply(p);
33246                 sb[sb.length] = st.apply(p);
33247             }else{
33248                 lb[lb.length] = ct.apply(p);
33249                 lsb[lsb.length] = st.apply(p);
33250             }
33251         }
33252         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33253                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33254     },
33255
33256     updateHeaders : function(){
33257         var html = this.renderHeaders();
33258         this.lockedHd.update(html[0]);
33259         this.mainHd.update(html[1]);
33260     },
33261
33262     /**
33263      * Focuses the specified row.
33264      * @param {Number} row The row index
33265      */
33266     focusRow : function(row)
33267     {
33268         //Roo.log('GridView.focusRow');
33269         var x = this.scroller.dom.scrollLeft;
33270         this.focusCell(row, 0, false);
33271         this.scroller.dom.scrollLeft = x;
33272     },
33273
33274     /**
33275      * Focuses the specified cell.
33276      * @param {Number} row The row index
33277      * @param {Number} col The column index
33278      * @param {Boolean} hscroll false to disable horizontal scrolling
33279      */
33280     focusCell : function(row, col, hscroll)
33281     {
33282         //Roo.log('GridView.focusCell');
33283         var el = this.ensureVisible(row, col, hscroll);
33284         this.focusEl.alignTo(el, "tl-tl");
33285         if(Roo.isGecko){
33286             this.focusEl.focus();
33287         }else{
33288             this.focusEl.focus.defer(1, this.focusEl);
33289         }
33290     },
33291
33292     /**
33293      * Scrolls the specified cell into view
33294      * @param {Number} row The row index
33295      * @param {Number} col The column index
33296      * @param {Boolean} hscroll false to disable horizontal scrolling
33297      */
33298     ensureVisible : function(row, col, hscroll)
33299     {
33300         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
33301         //return null; //disable for testing.
33302         if(typeof row != "number"){
33303             row = row.rowIndex;
33304         }
33305         if(row < 0 && row >= this.ds.getCount()){
33306             return  null;
33307         }
33308         col = (col !== undefined ? col : 0);
33309         var cm = this.grid.colModel;
33310         while(cm.isHidden(col)){
33311             col++;
33312         }
33313
33314         var el = this.getCell(row, col);
33315         if(!el){
33316             return null;
33317         }
33318         var c = this.scroller.dom;
33319
33320         var ctop = parseInt(el.offsetTop, 10);
33321         var cleft = parseInt(el.offsetLeft, 10);
33322         var cbot = ctop + el.offsetHeight;
33323         var cright = cleft + el.offsetWidth;
33324         
33325         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33326         var stop = parseInt(c.scrollTop, 10);
33327         var sleft = parseInt(c.scrollLeft, 10);
33328         var sbot = stop + ch;
33329         var sright = sleft + c.clientWidth;
33330         /*
33331         Roo.log('GridView.ensureVisible:' +
33332                 ' ctop:' + ctop +
33333                 ' c.clientHeight:' + c.clientHeight +
33334                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
33335                 ' stop:' + stop +
33336                 ' cbot:' + cbot +
33337                 ' sbot:' + sbot +
33338                 ' ch:' + ch  
33339                 );
33340         */
33341         if(ctop < stop){
33342              c.scrollTop = ctop;
33343             //Roo.log("set scrolltop to ctop DISABLE?");
33344         }else if(cbot > sbot){
33345             //Roo.log("set scrolltop to cbot-ch");
33346             c.scrollTop = cbot-ch;
33347         }
33348         
33349         if(hscroll !== false){
33350             if(cleft < sleft){
33351                 c.scrollLeft = cleft;
33352             }else if(cright > sright){
33353                 c.scrollLeft = cright-c.clientWidth;
33354             }
33355         }
33356          
33357         return el;
33358     },
33359
33360     updateColumns : function(){
33361         this.grid.stopEditing();
33362         var cm = this.grid.colModel, colIds = this.getColumnIds();
33363         //var totalWidth = cm.getTotalWidth();
33364         var pos = 0;
33365         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33366             //if(cm.isHidden(i)) continue;
33367             var w = cm.getColumnWidth(i);
33368             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33369             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33370         }
33371         this.updateSplitters();
33372     },
33373
33374     generateRules : function(cm){
33375         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33376         Roo.util.CSS.removeStyleSheet(rulesId);
33377         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33378             var cid = cm.getColumnId(i);
33379             var align = '';
33380             if(cm.config[i].align){
33381                 align = 'text-align:'+cm.config[i].align+';';
33382             }
33383             var hidden = '';
33384             if(cm.isHidden(i)){
33385                 hidden = 'display:none;';
33386             }
33387             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33388             ruleBuf.push(
33389                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33390                     this.hdSelector, cid, " {\n", align, width, "}\n",
33391                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
33392                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
33393         }
33394         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33395     },
33396
33397     updateSplitters : function(){
33398         var cm = this.cm, s = this.getSplitters();
33399         if(s){ // splitters not created yet
33400             var pos = 0, locked = true;
33401             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33402                 if(cm.isHidden(i)) continue;
33403                 var w = cm.getColumnWidth(i); // make sure it's a number
33404                 if(!cm.isLocked(i) && locked){
33405                     pos = 0;
33406                     locked = false;
33407                 }
33408                 pos += w;
33409                 s[i].style.left = (pos-this.splitOffset) + "px";
33410             }
33411         }
33412     },
33413
33414     handleHiddenChange : function(colModel, colIndex, hidden){
33415         if(hidden){
33416             this.hideColumn(colIndex);
33417         }else{
33418             this.unhideColumn(colIndex);
33419         }
33420     },
33421
33422     hideColumn : function(colIndex){
33423         var cid = this.getColumnId(colIndex);
33424         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33425         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33426         if(Roo.isSafari){
33427             this.updateHeaders();
33428         }
33429         this.updateSplitters();
33430         this.layout();
33431     },
33432
33433     unhideColumn : function(colIndex){
33434         var cid = this.getColumnId(colIndex);
33435         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33436         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33437
33438         if(Roo.isSafari){
33439             this.updateHeaders();
33440         }
33441         this.updateSplitters();
33442         this.layout();
33443     },
33444
33445     insertRows : function(dm, firstRow, lastRow, isUpdate){
33446         if(firstRow == 0 && lastRow == dm.getCount()-1){
33447             this.refresh();
33448         }else{
33449             if(!isUpdate){
33450                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33451             }
33452             var s = this.getScrollState();
33453             var markup = this.renderRows(firstRow, lastRow);
33454             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33455             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33456             this.restoreScroll(s);
33457             if(!isUpdate){
33458                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33459                 this.syncRowHeights(firstRow, lastRow);
33460                 this.stripeRows(firstRow);
33461                 this.layout();
33462             }
33463         }
33464     },
33465
33466     bufferRows : function(markup, target, index){
33467         var before = null, trows = target.rows, tbody = target.tBodies[0];
33468         if(index < trows.length){
33469             before = trows[index];
33470         }
33471         var b = document.createElement("div");
33472         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
33473         var rows = b.firstChild.rows;
33474         for(var i = 0, len = rows.length; i < len; i++){
33475             if(before){
33476                 tbody.insertBefore(rows[0], before);
33477             }else{
33478                 tbody.appendChild(rows[0]);
33479             }
33480         }
33481         b.innerHTML = "";
33482         b = null;
33483     },
33484
33485     deleteRows : function(dm, firstRow, lastRow){
33486         if(dm.getRowCount()<1){
33487             this.fireEvent("beforerefresh", this);
33488             this.mainBody.update("");
33489             this.lockedBody.update("");
33490             this.fireEvent("refresh", this);
33491         }else{
33492             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
33493             var bt = this.getBodyTable();
33494             var tbody = bt.firstChild;
33495             var rows = bt.rows;
33496             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
33497                 tbody.removeChild(rows[firstRow]);
33498             }
33499             this.stripeRows(firstRow);
33500             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
33501         }
33502     },
33503
33504     updateRows : function(dataSource, firstRow, lastRow){
33505         var s = this.getScrollState();
33506         this.refresh();
33507         this.restoreScroll(s);
33508     },
33509
33510     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
33511         if(!noRefresh){
33512            this.refresh();
33513         }
33514         this.updateHeaderSortState();
33515     },
33516
33517     getScrollState : function(){
33518         
33519         var sb = this.scroller.dom;
33520         return {left: sb.scrollLeft, top: sb.scrollTop};
33521     },
33522
33523     stripeRows : function(startRow){
33524         if(!this.grid.stripeRows || this.ds.getCount() < 1){
33525             return;
33526         }
33527         startRow = startRow || 0;
33528         var rows = this.getBodyTable().rows;
33529         var lrows = this.getLockedTable().rows;
33530         var cls = ' x-grid-row-alt ';
33531         for(var i = startRow, len = rows.length; i < len; i++){
33532             var row = rows[i], lrow = lrows[i];
33533             var isAlt = ((i+1) % 2 == 0);
33534             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
33535             if(isAlt == hasAlt){
33536                 continue;
33537             }
33538             if(isAlt){
33539                 row.className += " x-grid-row-alt";
33540             }else{
33541                 row.className = row.className.replace("x-grid-row-alt", "");
33542             }
33543             if(lrow){
33544                 lrow.className = row.className;
33545             }
33546         }
33547     },
33548
33549     restoreScroll : function(state){
33550         //Roo.log('GridView.restoreScroll');
33551         var sb = this.scroller.dom;
33552         sb.scrollLeft = state.left;
33553         sb.scrollTop = state.top;
33554         this.syncScroll();
33555     },
33556
33557     syncScroll : function(){
33558         //Roo.log('GridView.syncScroll');
33559         var sb = this.scroller.dom;
33560         var sh = this.mainHd.dom;
33561         var bs = this.mainBody.dom;
33562         var lv = this.lockedBody.dom;
33563         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
33564         lv.scrollTop = bs.scrollTop = sb.scrollTop;
33565     },
33566
33567     handleScroll : function(e){
33568         this.syncScroll();
33569         var sb = this.scroller.dom;
33570         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
33571         e.stopEvent();
33572     },
33573
33574     handleWheel : function(e){
33575         var d = e.getWheelDelta();
33576         this.scroller.dom.scrollTop -= d*22;
33577         // set this here to prevent jumpy scrolling on large tables
33578         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
33579         e.stopEvent();
33580     },
33581
33582     renderRows : function(startRow, endRow){
33583         // pull in all the crap needed to render rows
33584         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
33585         var colCount = cm.getColumnCount();
33586
33587         if(ds.getCount() < 1){
33588             return ["", ""];
33589         }
33590
33591         // build a map for all the columns
33592         var cs = [];
33593         for(var i = 0; i < colCount; i++){
33594             var name = cm.getDataIndex(i);
33595             cs[i] = {
33596                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
33597                 renderer : cm.getRenderer(i),
33598                 id : cm.getColumnId(i),
33599                 locked : cm.isLocked(i)
33600             };
33601         }
33602
33603         startRow = startRow || 0;
33604         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
33605
33606         // records to render
33607         var rs = ds.getRange(startRow, endRow);
33608
33609         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
33610     },
33611
33612     // As much as I hate to duplicate code, this was branched because FireFox really hates
33613     // [].join("") on strings. The performance difference was substantial enough to
33614     // branch this function
33615     doRender : Roo.isGecko ?
33616             function(cs, rs, ds, startRow, colCount, stripe){
33617                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33618                 // buffers
33619                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33620                 
33621                 var hasListener = this.grid.hasListener('rowclass');
33622                 var rowcfg = {};
33623                 for(var j = 0, len = rs.length; j < len; j++){
33624                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
33625                     for(var i = 0; i < colCount; i++){
33626                         c = cs[i];
33627                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33628                         p.id = c.id;
33629                         p.css = p.attr = "";
33630                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33631                         if(p.value == undefined || p.value === "") p.value = "&#160;";
33632                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
33633                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
33634                         }
33635                         var markup = ct.apply(p);
33636                         if(!c.locked){
33637                             cb+= markup;
33638                         }else{
33639                             lcb+= markup;
33640                         }
33641                     }
33642                     var alt = [];
33643                     if(stripe && ((rowIndex+1) % 2 == 0)){
33644                         alt.push("x-grid-row-alt")
33645                     }
33646                     if(r.dirty){
33647                         alt.push(  " x-grid-dirty-row");
33648                     }
33649                     rp.cells = lcb;
33650                     if(this.getRowClass){
33651                         alt.push(this.getRowClass(r, rowIndex));
33652                     }
33653                     if (hasListener) {
33654                         rowcfg = {
33655                              
33656                             record: r,
33657                             rowIndex : rowIndex,
33658                             rowClass : ''
33659                         }
33660                         this.grid.fireEvent('rowclass', this, rowcfg);
33661                         alt.push(rowcfg.rowClass);
33662                     }
33663                     rp.alt = alt.join(" ");
33664                     lbuf+= rt.apply(rp);
33665                     rp.cells = cb;
33666                     buf+=  rt.apply(rp);
33667                 }
33668                 return [lbuf, buf];
33669             } :
33670             function(cs, rs, ds, startRow, colCount, stripe){
33671                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33672                 // buffers
33673                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33674                 var hasListener = this.grid.hasListener('rowclass');
33675                 var rowcfg = {};
33676                 for(var j = 0, len = rs.length; j < len; j++){
33677                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
33678                     for(var i = 0; i < colCount; i++){
33679                         c = cs[i];
33680                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33681                         p.id = c.id;
33682                         p.css = p.attr = "";
33683                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33684                         if(p.value == undefined || p.value === "") p.value = "&#160;";
33685                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
33686                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
33687                         }
33688                         var markup = ct.apply(p);
33689                         if(!c.locked){
33690                             cb[cb.length] = markup;
33691                         }else{
33692                             lcb[lcb.length] = markup;
33693                         }
33694                     }
33695                     var alt = [];
33696                     if(stripe && ((rowIndex+1) % 2 == 0)){
33697                         alt.push( "x-grid-row-alt");
33698                     }
33699                     if(r.dirty){
33700                         alt.push(" x-grid-dirty-row");
33701                     }
33702                     rp.cells = lcb;
33703                     if(this.getRowClass){
33704                         alt.push( this.getRowClass(r, rowIndex));
33705                     }
33706                     if (hasListener) {
33707                         rowcfg = {
33708                              
33709                             record: r,
33710                             rowIndex : rowIndex,
33711                             rowClass : ''
33712                         }
33713                         this.grid.fireEvent('rowclass', this, rowcfg);
33714                         alt.push(rowcfg.rowClass);
33715                     }
33716                     rp.alt = alt.join(" ");
33717                     rp.cells = lcb.join("");
33718                     lbuf[lbuf.length] = rt.apply(rp);
33719                     rp.cells = cb.join("");
33720                     buf[buf.length] =  rt.apply(rp);
33721                 }
33722                 return [lbuf.join(""), buf.join("")];
33723             },
33724
33725     renderBody : function(){
33726         var markup = this.renderRows();
33727         var bt = this.templates.body;
33728         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
33729     },
33730
33731     /**
33732      * Refreshes the grid
33733      * @param {Boolean} headersToo
33734      */
33735     refresh : function(headersToo){
33736         this.fireEvent("beforerefresh", this);
33737         this.grid.stopEditing();
33738         var result = this.renderBody();
33739         this.lockedBody.update(result[0]);
33740         this.mainBody.update(result[1]);
33741         if(headersToo === true){
33742             this.updateHeaders();
33743             this.updateColumns();
33744             this.updateSplitters();
33745             this.updateHeaderSortState();
33746         }
33747         this.syncRowHeights();
33748         this.layout();
33749         this.fireEvent("refresh", this);
33750     },
33751
33752     handleColumnMove : function(cm, oldIndex, newIndex){
33753         this.indexMap = null;
33754         var s = this.getScrollState();
33755         this.refresh(true);
33756         this.restoreScroll(s);
33757         this.afterMove(newIndex);
33758     },
33759
33760     afterMove : function(colIndex){
33761         if(this.enableMoveAnim && Roo.enableFx){
33762             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
33763         }
33764     },
33765
33766     updateCell : function(dm, rowIndex, dataIndex){
33767         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
33768         if(typeof colIndex == "undefined"){ // not present in grid
33769             return;
33770         }
33771         var cm = this.grid.colModel;
33772         var cell = this.getCell(rowIndex, colIndex);
33773         var cellText = this.getCellText(rowIndex, colIndex);
33774
33775         var p = {
33776             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
33777             id : cm.getColumnId(colIndex),
33778             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
33779         };
33780         var renderer = cm.getRenderer(colIndex);
33781         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
33782         if(typeof val == "undefined" || val === "") val = "&#160;";
33783         cellText.innerHTML = val;
33784         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
33785         this.syncRowHeights(rowIndex, rowIndex);
33786     },
33787
33788     calcColumnWidth : function(colIndex, maxRowsToMeasure){
33789         var maxWidth = 0;
33790         if(this.grid.autoSizeHeaders){
33791             var h = this.getHeaderCellMeasure(colIndex);
33792             maxWidth = Math.max(maxWidth, h.scrollWidth);
33793         }
33794         var tb, index;
33795         if(this.cm.isLocked(colIndex)){
33796             tb = this.getLockedTable();
33797             index = colIndex;
33798         }else{
33799             tb = this.getBodyTable();
33800             index = colIndex - this.cm.getLockedCount();
33801         }
33802         if(tb && tb.rows){
33803             var rows = tb.rows;
33804             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
33805             for(var i = 0; i < stopIndex; i++){
33806                 var cell = rows[i].childNodes[index].firstChild;
33807                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
33808             }
33809         }
33810         return maxWidth + /*margin for error in IE*/ 5;
33811     },
33812     /**
33813      * Autofit a column to its content.
33814      * @param {Number} colIndex
33815      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
33816      */
33817      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
33818          if(this.cm.isHidden(colIndex)){
33819              return; // can't calc a hidden column
33820          }
33821         if(forceMinSize){
33822             var cid = this.cm.getColumnId(colIndex);
33823             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
33824            if(this.grid.autoSizeHeaders){
33825                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
33826            }
33827         }
33828         var newWidth = this.calcColumnWidth(colIndex);
33829         this.cm.setColumnWidth(colIndex,
33830             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
33831         if(!suppressEvent){
33832             this.grid.fireEvent("columnresize", colIndex, newWidth);
33833         }
33834     },
33835
33836     /**
33837      * Autofits all columns to their content and then expands to fit any extra space in the grid
33838      */
33839      autoSizeColumns : function(){
33840         var cm = this.grid.colModel;
33841         var colCount = cm.getColumnCount();
33842         for(var i = 0; i < colCount; i++){
33843             this.autoSizeColumn(i, true, true);
33844         }
33845         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
33846             this.fitColumns();
33847         }else{
33848             this.updateColumns();
33849             this.layout();
33850         }
33851     },
33852
33853     /**
33854      * Autofits all columns to the grid's width proportionate with their current size
33855      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
33856      */
33857     fitColumns : function(reserveScrollSpace){
33858         var cm = this.grid.colModel;
33859         var colCount = cm.getColumnCount();
33860         var cols = [];
33861         var width = 0;
33862         var i, w;
33863         for (i = 0; i < colCount; i++){
33864             if(!cm.isHidden(i) && !cm.isFixed(i)){
33865                 w = cm.getColumnWidth(i);
33866                 cols.push(i);
33867                 cols.push(w);
33868                 width += w;
33869             }
33870         }
33871         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
33872         if(reserveScrollSpace){
33873             avail -= 17;
33874         }
33875         var frac = (avail - cm.getTotalWidth())/width;
33876         while (cols.length){
33877             w = cols.pop();
33878             i = cols.pop();
33879             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
33880         }
33881         this.updateColumns();
33882         this.layout();
33883     },
33884
33885     onRowSelect : function(rowIndex){
33886         var row = this.getRowComposite(rowIndex);
33887         row.addClass("x-grid-row-selected");
33888     },
33889
33890     onRowDeselect : function(rowIndex){
33891         var row = this.getRowComposite(rowIndex);
33892         row.removeClass("x-grid-row-selected");
33893     },
33894
33895     onCellSelect : function(row, col){
33896         var cell = this.getCell(row, col);
33897         if(cell){
33898             Roo.fly(cell).addClass("x-grid-cell-selected");
33899         }
33900     },
33901
33902     onCellDeselect : function(row, col){
33903         var cell = this.getCell(row, col);
33904         if(cell){
33905             Roo.fly(cell).removeClass("x-grid-cell-selected");
33906         }
33907     },
33908
33909     updateHeaderSortState : function(){
33910         var state = this.ds.getSortState();
33911         if(!state){
33912             return;
33913         }
33914         this.sortState = state;
33915         var sortColumn = this.cm.findColumnIndex(state.field);
33916         if(sortColumn != -1){
33917             var sortDir = state.direction;
33918             var sc = this.sortClasses;
33919             var hds = this.el.select(this.headerSelector).removeClass(sc);
33920             hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
33921         }
33922     },
33923
33924     handleHeaderClick : function(g, index){
33925         if(this.headersDisabled){
33926             return;
33927         }
33928         var dm = g.dataSource, cm = g.colModel;
33929         if(!cm.isSortable(index)){
33930             return;
33931         }
33932         g.stopEditing();
33933         dm.sort(cm.getDataIndex(index));
33934     },
33935
33936
33937     destroy : function(){
33938         if(this.colMenu){
33939             this.colMenu.removeAll();
33940             Roo.menu.MenuMgr.unregister(this.colMenu);
33941             this.colMenu.getEl().remove();
33942             delete this.colMenu;
33943         }
33944         if(this.hmenu){
33945             this.hmenu.removeAll();
33946             Roo.menu.MenuMgr.unregister(this.hmenu);
33947             this.hmenu.getEl().remove();
33948             delete this.hmenu;
33949         }
33950         if(this.grid.enableColumnMove){
33951             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
33952             if(dds){
33953                 for(var dd in dds){
33954                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
33955                         var elid = dds[dd].dragElId;
33956                         dds[dd].unreg();
33957                         Roo.get(elid).remove();
33958                     } else if(dds[dd].config.isTarget){
33959                         dds[dd].proxyTop.remove();
33960                         dds[dd].proxyBottom.remove();
33961                         dds[dd].unreg();
33962                     }
33963                     if(Roo.dd.DDM.locationCache[dd]){
33964                         delete Roo.dd.DDM.locationCache[dd];
33965                     }
33966                 }
33967                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
33968             }
33969         }
33970         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
33971         this.bind(null, null);
33972         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
33973     },
33974
33975     handleLockChange : function(){
33976         this.refresh(true);
33977     },
33978
33979     onDenyColumnLock : function(){
33980
33981     },
33982
33983     onDenyColumnHide : function(){
33984
33985     },
33986
33987     handleHdMenuClick : function(item){
33988         var index = this.hdCtxIndex;
33989         var cm = this.cm, ds = this.ds;
33990         switch(item.id){
33991             case "asc":
33992                 ds.sort(cm.getDataIndex(index), "ASC");
33993                 break;
33994             case "desc":
33995                 ds.sort(cm.getDataIndex(index), "DESC");
33996                 break;
33997             case "lock":
33998                 var lc = cm.getLockedCount();
33999                 if(cm.getColumnCount(true) <= lc+1){
34000                     this.onDenyColumnLock();
34001                     return;
34002                 }
34003                 if(lc != index){
34004                     cm.setLocked(index, true, true);
34005                     cm.moveColumn(index, lc);
34006                     this.grid.fireEvent("columnmove", index, lc);
34007                 }else{
34008                     cm.setLocked(index, true);
34009                 }
34010             break;
34011             case "unlock":
34012                 var lc = cm.getLockedCount();
34013                 if((lc-1) != index){
34014                     cm.setLocked(index, false, true);
34015                     cm.moveColumn(index, lc-1);
34016                     this.grid.fireEvent("columnmove", index, lc-1);
34017                 }else{
34018                     cm.setLocked(index, false);
34019                 }
34020             break;
34021             default:
34022                 index = cm.getIndexById(item.id.substr(4));
34023                 if(index != -1){
34024                     if(item.checked && cm.getColumnCount(true) <= 1){
34025                         this.onDenyColumnHide();
34026                         return false;
34027                     }
34028                     cm.setHidden(index, item.checked);
34029                 }
34030         }
34031         return true;
34032     },
34033
34034     beforeColMenuShow : function(){
34035         var cm = this.cm,  colCount = cm.getColumnCount();
34036         this.colMenu.removeAll();
34037         for(var i = 0; i < colCount; i++){
34038             this.colMenu.add(new Roo.menu.CheckItem({
34039                 id: "col-"+cm.getColumnId(i),
34040                 text: cm.getColumnHeader(i),
34041                 checked: !cm.isHidden(i),
34042                 hideOnClick:false
34043             }));
34044         }
34045     },
34046
34047     handleHdCtx : function(g, index, e){
34048         e.stopEvent();
34049         var hd = this.getHeaderCell(index);
34050         this.hdCtxIndex = index;
34051         var ms = this.hmenu.items, cm = this.cm;
34052         ms.get("asc").setDisabled(!cm.isSortable(index));
34053         ms.get("desc").setDisabled(!cm.isSortable(index));
34054         if(this.grid.enableColLock !== false){
34055             ms.get("lock").setDisabled(cm.isLocked(index));
34056             ms.get("unlock").setDisabled(!cm.isLocked(index));
34057         }
34058         this.hmenu.show(hd, "tl-bl");
34059     },
34060
34061     handleHdOver : function(e){
34062         var hd = this.findHeaderCell(e.getTarget());
34063         if(hd && !this.headersDisabled){
34064             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34065                this.fly(hd).addClass("x-grid-hd-over");
34066             }
34067         }
34068     },
34069
34070     handleHdOut : function(e){
34071         var hd = this.findHeaderCell(e.getTarget());
34072         if(hd){
34073             this.fly(hd).removeClass("x-grid-hd-over");
34074         }
34075     },
34076
34077     handleSplitDblClick : function(e, t){
34078         var i = this.getCellIndex(t);
34079         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34080             this.autoSizeColumn(i, true);
34081             this.layout();
34082         }
34083     },
34084
34085     render : function(){
34086
34087         var cm = this.cm;
34088         var colCount = cm.getColumnCount();
34089
34090         if(this.grid.monitorWindowResize === true){
34091             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34092         }
34093         var header = this.renderHeaders();
34094         var body = this.templates.body.apply({rows:""});
34095         var html = this.templates.master.apply({
34096             lockedBody: body,
34097             body: body,
34098             lockedHeader: header[0],
34099             header: header[1]
34100         });
34101
34102         //this.updateColumns();
34103
34104         this.grid.getGridEl().dom.innerHTML = html;
34105
34106         this.initElements();
34107         
34108         // a kludge to fix the random scolling effect in webkit
34109         this.el.on("scroll", function() {
34110             this.el.dom.scrollTop=0; // hopefully not recursive..
34111         },this);
34112
34113         this.scroller.on("scroll", this.handleScroll, this);
34114         this.lockedBody.on("mousewheel", this.handleWheel, this);
34115         this.mainBody.on("mousewheel", this.handleWheel, this);
34116
34117         this.mainHd.on("mouseover", this.handleHdOver, this);
34118         this.mainHd.on("mouseout", this.handleHdOut, this);
34119         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34120                 {delegate: "."+this.splitClass});
34121
34122         this.lockedHd.on("mouseover", this.handleHdOver, this);
34123         this.lockedHd.on("mouseout", this.handleHdOut, this);
34124         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34125                 {delegate: "."+this.splitClass});
34126
34127         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34128             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34129         }
34130
34131         this.updateSplitters();
34132
34133         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34134             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34135             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34136         }
34137
34138         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34139             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34140             this.hmenu.add(
34141                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34142                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34143             );
34144             if(this.grid.enableColLock !== false){
34145                 this.hmenu.add('-',
34146                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34147                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34148                 );
34149             }
34150             if(this.grid.enableColumnHide !== false){
34151
34152                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34153                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34154                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34155
34156                 this.hmenu.add('-',
34157                     {id:"columns", text: this.columnsText, menu: this.colMenu}
34158                 );
34159             }
34160             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34161
34162             this.grid.on("headercontextmenu", this.handleHdCtx, this);
34163         }
34164
34165         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34166             this.dd = new Roo.grid.GridDragZone(this.grid, {
34167                 ddGroup : this.grid.ddGroup || 'GridDD'
34168             });
34169         }
34170
34171         /*
34172         for(var i = 0; i < colCount; i++){
34173             if(cm.isHidden(i)){
34174                 this.hideColumn(i);
34175             }
34176             if(cm.config[i].align){
34177                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34178                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34179             }
34180         }*/
34181         
34182         this.updateHeaderSortState();
34183
34184         this.beforeInitialResize();
34185         this.layout(true);
34186
34187         // two part rendering gives faster view to the user
34188         this.renderPhase2.defer(1, this);
34189     },
34190
34191     renderPhase2 : function(){
34192         // render the rows now
34193         this.refresh();
34194         if(this.grid.autoSizeColumns){
34195             this.autoSizeColumns();
34196         }
34197     },
34198
34199     beforeInitialResize : function(){
34200
34201     },
34202
34203     onColumnSplitterMoved : function(i, w){
34204         this.userResized = true;
34205         var cm = this.grid.colModel;
34206         cm.setColumnWidth(i, w, true);
34207         var cid = cm.getColumnId(i);
34208         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34209         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34210         this.updateSplitters();
34211         this.layout();
34212         this.grid.fireEvent("columnresize", i, w);
34213     },
34214
34215     syncRowHeights : function(startIndex, endIndex){
34216         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34217             startIndex = startIndex || 0;
34218             var mrows = this.getBodyTable().rows;
34219             var lrows = this.getLockedTable().rows;
34220             var len = mrows.length-1;
34221             endIndex = Math.min(endIndex || len, len);
34222             for(var i = startIndex; i <= endIndex; i++){
34223                 var m = mrows[i], l = lrows[i];
34224                 var h = Math.max(m.offsetHeight, l.offsetHeight);
34225                 m.style.height = l.style.height = h + "px";
34226             }
34227         }
34228     },
34229
34230     layout : function(initialRender, is2ndPass){
34231         var g = this.grid;
34232         var auto = g.autoHeight;
34233         var scrollOffset = 16;
34234         var c = g.getGridEl(), cm = this.cm,
34235                 expandCol = g.autoExpandColumn,
34236                 gv = this;
34237         //c.beginMeasure();
34238
34239         if(!c.dom.offsetWidth){ // display:none?
34240             if(initialRender){
34241                 this.lockedWrap.show();
34242                 this.mainWrap.show();
34243             }
34244             return;
34245         }
34246
34247         var hasLock = this.cm.isLocked(0);
34248
34249         var tbh = this.headerPanel.getHeight();
34250         var bbh = this.footerPanel.getHeight();
34251
34252         if(auto){
34253             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34254             var newHeight = ch + c.getBorderWidth("tb");
34255             if(g.maxHeight){
34256                 newHeight = Math.min(g.maxHeight, newHeight);
34257             }
34258             c.setHeight(newHeight);
34259         }
34260
34261         if(g.autoWidth){
34262             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34263         }
34264
34265         var s = this.scroller;
34266
34267         var csize = c.getSize(true);
34268
34269         this.el.setSize(csize.width, csize.height);
34270
34271         this.headerPanel.setWidth(csize.width);
34272         this.footerPanel.setWidth(csize.width);
34273
34274         var hdHeight = this.mainHd.getHeight();
34275         var vw = csize.width;
34276         var vh = csize.height - (tbh + bbh);
34277
34278         s.setSize(vw, vh);
34279
34280         var bt = this.getBodyTable();
34281         var ltWidth = hasLock ?
34282                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34283
34284         var scrollHeight = bt.offsetHeight;
34285         var scrollWidth = ltWidth + bt.offsetWidth;
34286         var vscroll = false, hscroll = false;
34287
34288         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34289
34290         var lw = this.lockedWrap, mw = this.mainWrap;
34291         var lb = this.lockedBody, mb = this.mainBody;
34292
34293         setTimeout(function(){
34294             var t = s.dom.offsetTop;
34295             var w = s.dom.clientWidth,
34296                 h = s.dom.clientHeight;
34297
34298             lw.setTop(t);
34299             lw.setSize(ltWidth, h);
34300
34301             mw.setLeftTop(ltWidth, t);
34302             mw.setSize(w-ltWidth, h);
34303
34304             lb.setHeight(h-hdHeight);
34305             mb.setHeight(h-hdHeight);
34306
34307             if(is2ndPass !== true && !gv.userResized && expandCol){
34308                 // high speed resize without full column calculation
34309                 
34310                 var ci = cm.getIndexById(expandCol);
34311                 if (ci < 0) {
34312                     ci = cm.findColumnIndex(expandCol);
34313                 }
34314                 ci = Math.max(0, ci); // make sure it's got at least the first col.
34315                 var expandId = cm.getColumnId(ci);
34316                 var  tw = cm.getTotalWidth(false);
34317                 var currentWidth = cm.getColumnWidth(ci);
34318                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34319                 if(currentWidth != cw){
34320                     cm.setColumnWidth(ci, cw, true);
34321                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34322                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34323                     gv.updateSplitters();
34324                     gv.layout(false, true);
34325                 }
34326             }
34327
34328             if(initialRender){
34329                 lw.show();
34330                 mw.show();
34331             }
34332             //c.endMeasure();
34333         }, 10);
34334     },
34335
34336     onWindowResize : function(){
34337         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34338             return;
34339         }
34340         this.layout();
34341     },
34342
34343     appendFooter : function(parentEl){
34344         return null;
34345     },
34346
34347     sortAscText : "Sort Ascending",
34348     sortDescText : "Sort Descending",
34349     lockText : "Lock Column",
34350     unlockText : "Unlock Column",
34351     columnsText : "Columns"
34352 });
34353
34354
34355 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
34356     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
34357     this.proxy.el.addClass('x-grid3-col-dd');
34358 };
34359
34360 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
34361     handleMouseDown : function(e){
34362
34363     },
34364
34365     callHandleMouseDown : function(e){
34366         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
34367     }
34368 });
34369 /*
34370  * Based on:
34371  * Ext JS Library 1.1.1
34372  * Copyright(c) 2006-2007, Ext JS, LLC.
34373  *
34374  * Originally Released Under LGPL - original licence link has changed is not relivant.
34375  *
34376  * Fork - LGPL
34377  * <script type="text/javascript">
34378  */
34379  
34380 // private
34381 // This is a support class used internally by the Grid components
34382 Roo.grid.SplitDragZone = function(grid, hd, hd2){
34383     this.grid = grid;
34384     this.view = grid.getView();
34385     this.proxy = this.view.resizeProxy;
34386     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
34387         "gridSplitters" + this.grid.getGridEl().id, {
34388         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
34389     });
34390     this.setHandleElId(Roo.id(hd));
34391     this.setOuterHandleElId(Roo.id(hd2));
34392     this.scroll = false;
34393 };
34394 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
34395     fly: Roo.Element.fly,
34396
34397     b4StartDrag : function(x, y){
34398         this.view.headersDisabled = true;
34399         this.proxy.setHeight(this.view.mainWrap.getHeight());
34400         var w = this.cm.getColumnWidth(this.cellIndex);
34401         var minw = Math.max(w-this.grid.minColumnWidth, 0);
34402         this.resetConstraints();
34403         this.setXConstraint(minw, 1000);
34404         this.setYConstraint(0, 0);
34405         this.minX = x - minw;
34406         this.maxX = x + 1000;
34407         this.startPos = x;
34408         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
34409     },
34410
34411
34412     handleMouseDown : function(e){
34413         ev = Roo.EventObject.setEvent(e);
34414         var t = this.fly(ev.getTarget());
34415         if(t.hasClass("x-grid-split")){
34416             this.cellIndex = this.view.getCellIndex(t.dom);
34417             this.split = t.dom;
34418             this.cm = this.grid.colModel;
34419             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
34420                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
34421             }
34422         }
34423     },
34424
34425     endDrag : function(e){
34426         this.view.headersDisabled = false;
34427         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
34428         var diff = endX - this.startPos;
34429         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
34430     },
34431
34432     autoOffset : function(){
34433         this.setDelta(0,0);
34434     }
34435 });/*
34436  * Based on:
34437  * Ext JS Library 1.1.1
34438  * Copyright(c) 2006-2007, Ext JS, LLC.
34439  *
34440  * Originally Released Under LGPL - original licence link has changed is not relivant.
34441  *
34442  * Fork - LGPL
34443  * <script type="text/javascript">
34444  */
34445  
34446 // private
34447 // This is a support class used internally by the Grid components
34448 Roo.grid.GridDragZone = function(grid, config){
34449     this.view = grid.getView();
34450     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
34451     if(this.view.lockedBody){
34452         this.setHandleElId(Roo.id(this.view.mainBody.dom));
34453         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
34454     }
34455     this.scroll = false;
34456     this.grid = grid;
34457     this.ddel = document.createElement('div');
34458     this.ddel.className = 'x-grid-dd-wrap';
34459 };
34460
34461 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
34462     ddGroup : "GridDD",
34463
34464     getDragData : function(e){
34465         var t = Roo.lib.Event.getTarget(e);
34466         var rowIndex = this.view.findRowIndex(t);
34467         if(rowIndex !== false){
34468             var sm = this.grid.selModel;
34469             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
34470               //  sm.mouseDown(e, t);
34471             //}
34472             if (e.hasModifier()){
34473                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
34474             }
34475             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
34476         }
34477         return false;
34478     },
34479
34480     onInitDrag : function(e){
34481         var data = this.dragData;
34482         this.ddel.innerHTML = this.grid.getDragDropText();
34483         this.proxy.update(this.ddel);
34484         // fire start drag?
34485     },
34486
34487     afterRepair : function(){
34488         this.dragging = false;
34489     },
34490
34491     getRepairXY : function(e, data){
34492         return false;
34493     },
34494
34495     onEndDrag : function(data, e){
34496         // fire end drag?
34497     },
34498
34499     onValidDrop : function(dd, e, id){
34500         // fire drag drop?
34501         this.hideProxy();
34502     },
34503
34504     beforeInvalidDrop : function(e, id){
34505
34506     }
34507 });/*
34508  * Based on:
34509  * Ext JS Library 1.1.1
34510  * Copyright(c) 2006-2007, Ext JS, LLC.
34511  *
34512  * Originally Released Under LGPL - original licence link has changed is not relivant.
34513  *
34514  * Fork - LGPL
34515  * <script type="text/javascript">
34516  */
34517  
34518
34519 /**
34520  * @class Roo.grid.ColumnModel
34521  * @extends Roo.util.Observable
34522  * This is the default implementation of a ColumnModel used by the Grid. It defines
34523  * the columns in the grid.
34524  * <br>Usage:<br>
34525  <pre><code>
34526  var colModel = new Roo.grid.ColumnModel([
34527         {header: "Ticker", width: 60, sortable: true, locked: true},
34528         {header: "Company Name", width: 150, sortable: true},
34529         {header: "Market Cap.", width: 100, sortable: true},
34530         {header: "$ Sales", width: 100, sortable: true, renderer: money},
34531         {header: "Employees", width: 100, sortable: true, resizable: false}
34532  ]);
34533  </code></pre>
34534  * <p>
34535  
34536  * The config options listed for this class are options which may appear in each
34537  * individual column definition.
34538  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
34539  * @constructor
34540  * @param {Object} config An Array of column config objects. See this class's
34541  * config objects for details.
34542 */
34543 Roo.grid.ColumnModel = function(config){
34544         /**
34545      * The config passed into the constructor
34546      */
34547     this.config = config;
34548     this.lookup = {};
34549
34550     // if no id, create one
34551     // if the column does not have a dataIndex mapping,
34552     // map it to the order it is in the config
34553     for(var i = 0, len = config.length; i < len; i++){
34554         var c = config[i];
34555         if(typeof c.dataIndex == "undefined"){
34556             c.dataIndex = i;
34557         }
34558         if(typeof c.renderer == "string"){
34559             c.renderer = Roo.util.Format[c.renderer];
34560         }
34561         if(typeof c.id == "undefined"){
34562             c.id = Roo.id();
34563         }
34564         if(c.editor && c.editor.xtype){
34565             c.editor  = Roo.factory(c.editor, Roo.grid);
34566         }
34567         if(c.editor && c.editor.isFormField){
34568             c.editor = new Roo.grid.GridEditor(c.editor);
34569         }
34570         this.lookup[c.id] = c;
34571     }
34572
34573     /**
34574      * The width of columns which have no width specified (defaults to 100)
34575      * @type Number
34576      */
34577     this.defaultWidth = 100;
34578
34579     /**
34580      * Default sortable of columns which have no sortable specified (defaults to false)
34581      * @type Boolean
34582      */
34583     this.defaultSortable = false;
34584
34585     this.addEvents({
34586         /**
34587              * @event widthchange
34588              * Fires when the width of a column changes.
34589              * @param {ColumnModel} this
34590              * @param {Number} columnIndex The column index
34591              * @param {Number} newWidth The new width
34592              */
34593             "widthchange": true,
34594         /**
34595              * @event headerchange
34596              * Fires when the text of a header changes.
34597              * @param {ColumnModel} this
34598              * @param {Number} columnIndex The column index
34599              * @param {Number} newText The new header text
34600              */
34601             "headerchange": true,
34602         /**
34603              * @event hiddenchange
34604              * Fires when a column is hidden or "unhidden".
34605              * @param {ColumnModel} this
34606              * @param {Number} columnIndex The column index
34607              * @param {Boolean} hidden true if hidden, false otherwise
34608              */
34609             "hiddenchange": true,
34610             /**
34611          * @event columnmoved
34612          * Fires when a column is moved.
34613          * @param {ColumnModel} this
34614          * @param {Number} oldIndex
34615          * @param {Number} newIndex
34616          */
34617         "columnmoved" : true,
34618         /**
34619          * @event columlockchange
34620          * Fires when a column's locked state is changed
34621          * @param {ColumnModel} this
34622          * @param {Number} colIndex
34623          * @param {Boolean} locked true if locked
34624          */
34625         "columnlockchange" : true
34626     });
34627     Roo.grid.ColumnModel.superclass.constructor.call(this);
34628 };
34629 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
34630     /**
34631      * @cfg {String} header The header text to display in the Grid view.
34632      */
34633     /**
34634      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
34635      * {@link Roo.data.Record} definition from which to draw the column's value. If not
34636      * specified, the column's index is used as an index into the Record's data Array.
34637      */
34638     /**
34639      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
34640      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
34641      */
34642     /**
34643      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
34644      * Defaults to the value of the {@link #defaultSortable} property.
34645      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
34646      */
34647     /**
34648      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
34649      */
34650     /**
34651      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
34652      */
34653     /**
34654      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
34655      */
34656     /**
34657      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
34658      */
34659     /**
34660      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
34661      * given the cell's data value. See {@link #setRenderer}. If not specified, the
34662      * default renderer uses the raw data value.
34663      */
34664        /**
34665      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
34666      */
34667     /**
34668      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
34669      */
34670
34671     /**
34672      * Returns the id of the column at the specified index.
34673      * @param {Number} index The column index
34674      * @return {String} the id
34675      */
34676     getColumnId : function(index){
34677         return this.config[index].id;
34678     },
34679
34680     /**
34681      * Returns the column for a specified id.
34682      * @param {String} id The column id
34683      * @return {Object} the column
34684      */
34685     getColumnById : function(id){
34686         return this.lookup[id];
34687     },
34688
34689     
34690     /**
34691      * Returns the column for a specified dataIndex.
34692      * @param {String} dataIndex The column dataIndex
34693      * @return {Object|Boolean} the column or false if not found
34694      */
34695     getColumnByDataIndex: function(dataIndex){
34696         var index = this.findColumnIndex(dataIndex);
34697         return index > -1 ? this.config[index] : false;
34698     },
34699     
34700     /**
34701      * Returns the index for a specified column id.
34702      * @param {String} id The column id
34703      * @return {Number} the index, or -1 if not found
34704      */
34705     getIndexById : function(id){
34706         for(var i = 0, len = this.config.length; i < len; i++){
34707             if(this.config[i].id == id){
34708                 return i;
34709             }
34710         }
34711         return -1;
34712     },
34713     
34714     /**
34715      * Returns the index for a specified column dataIndex.
34716      * @param {String} dataIndex The column dataIndex
34717      * @return {Number} the index, or -1 if not found
34718      */
34719     
34720     findColumnIndex : function(dataIndex){
34721         for(var i = 0, len = this.config.length; i < len; i++){
34722             if(this.config[i].dataIndex == dataIndex){
34723                 return i;
34724             }
34725         }
34726         return -1;
34727     },
34728     
34729     
34730     moveColumn : function(oldIndex, newIndex){
34731         var c = this.config[oldIndex];
34732         this.config.splice(oldIndex, 1);
34733         this.config.splice(newIndex, 0, c);
34734         this.dataMap = null;
34735         this.fireEvent("columnmoved", this, oldIndex, newIndex);
34736     },
34737
34738     isLocked : function(colIndex){
34739         return this.config[colIndex].locked === true;
34740     },
34741
34742     setLocked : function(colIndex, value, suppressEvent){
34743         if(this.isLocked(colIndex) == value){
34744             return;
34745         }
34746         this.config[colIndex].locked = value;
34747         if(!suppressEvent){
34748             this.fireEvent("columnlockchange", this, colIndex, value);
34749         }
34750     },
34751
34752     getTotalLockedWidth : function(){
34753         var totalWidth = 0;
34754         for(var i = 0; i < this.config.length; i++){
34755             if(this.isLocked(i) && !this.isHidden(i)){
34756                 this.totalWidth += this.getColumnWidth(i);
34757             }
34758         }
34759         return totalWidth;
34760     },
34761
34762     getLockedCount : function(){
34763         for(var i = 0, len = this.config.length; i < len; i++){
34764             if(!this.isLocked(i)){
34765                 return i;
34766             }
34767         }
34768     },
34769
34770     /**
34771      * Returns the number of columns.
34772      * @return {Number}
34773      */
34774     getColumnCount : function(visibleOnly){
34775         if(visibleOnly === true){
34776             var c = 0;
34777             for(var i = 0, len = this.config.length; i < len; i++){
34778                 if(!this.isHidden(i)){
34779                     c++;
34780                 }
34781             }
34782             return c;
34783         }
34784         return this.config.length;
34785     },
34786
34787     /**
34788      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
34789      * @param {Function} fn
34790      * @param {Object} scope (optional)
34791      * @return {Array} result
34792      */
34793     getColumnsBy : function(fn, scope){
34794         var r = [];
34795         for(var i = 0, len = this.config.length; i < len; i++){
34796             var c = this.config[i];
34797             if(fn.call(scope||this, c, i) === true){
34798                 r[r.length] = c;
34799             }
34800         }
34801         return r;
34802     },
34803
34804     /**
34805      * Returns true if the specified column is sortable.
34806      * @param {Number} col The column index
34807      * @return {Boolean}
34808      */
34809     isSortable : function(col){
34810         if(typeof this.config[col].sortable == "undefined"){
34811             return this.defaultSortable;
34812         }
34813         return this.config[col].sortable;
34814     },
34815
34816     /**
34817      * Returns the rendering (formatting) function defined for the column.
34818      * @param {Number} col The column index.
34819      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
34820      */
34821     getRenderer : function(col){
34822         if(!this.config[col].renderer){
34823             return Roo.grid.ColumnModel.defaultRenderer;
34824         }
34825         return this.config[col].renderer;
34826     },
34827
34828     /**
34829      * Sets the rendering (formatting) function for a column.
34830      * @param {Number} col The column index
34831      * @param {Function} fn The function to use to process the cell's raw data
34832      * to return HTML markup for the grid view. The render function is called with
34833      * the following parameters:<ul>
34834      * <li>Data value.</li>
34835      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
34836      * <li>css A CSS style string to apply to the table cell.</li>
34837      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
34838      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
34839      * <li>Row index</li>
34840      * <li>Column index</li>
34841      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
34842      */
34843     setRenderer : function(col, fn){
34844         this.config[col].renderer = fn;
34845     },
34846
34847     /**
34848      * Returns the width for the specified column.
34849      * @param {Number} col The column index
34850      * @return {Number}
34851      */
34852     getColumnWidth : function(col){
34853         return this.config[col].width * 1 || this.defaultWidth;
34854     },
34855
34856     /**
34857      * Sets the width for a column.
34858      * @param {Number} col The column index
34859      * @param {Number} width The new width
34860      */
34861     setColumnWidth : function(col, width, suppressEvent){
34862         this.config[col].width = width;
34863         this.totalWidth = null;
34864         if(!suppressEvent){
34865              this.fireEvent("widthchange", this, col, width);
34866         }
34867     },
34868
34869     /**
34870      * Returns the total width of all columns.
34871      * @param {Boolean} includeHidden True to include hidden column widths
34872      * @return {Number}
34873      */
34874     getTotalWidth : function(includeHidden){
34875         if(!this.totalWidth){
34876             this.totalWidth = 0;
34877             for(var i = 0, len = this.config.length; i < len; i++){
34878                 if(includeHidden || !this.isHidden(i)){
34879                     this.totalWidth += this.getColumnWidth(i);
34880                 }
34881             }
34882         }
34883         return this.totalWidth;
34884     },
34885
34886     /**
34887      * Returns the header for the specified column.
34888      * @param {Number} col The column index
34889      * @return {String}
34890      */
34891     getColumnHeader : function(col){
34892         return this.config[col].header;
34893     },
34894
34895     /**
34896      * Sets the header for a column.
34897      * @param {Number} col The column index
34898      * @param {String} header The new header
34899      */
34900     setColumnHeader : function(col, header){
34901         this.config[col].header = header;
34902         this.fireEvent("headerchange", this, col, header);
34903     },
34904
34905     /**
34906      * Returns the tooltip for the specified column.
34907      * @param {Number} col The column index
34908      * @return {String}
34909      */
34910     getColumnTooltip : function(col){
34911             return this.config[col].tooltip;
34912     },
34913     /**
34914      * Sets the tooltip for a column.
34915      * @param {Number} col The column index
34916      * @param {String} tooltip The new tooltip
34917      */
34918     setColumnTooltip : function(col, tooltip){
34919             this.config[col].tooltip = tooltip;
34920     },
34921
34922     /**
34923      * Returns the dataIndex for the specified column.
34924      * @param {Number} col The column index
34925      * @return {Number}
34926      */
34927     getDataIndex : function(col){
34928         return this.config[col].dataIndex;
34929     },
34930
34931     /**
34932      * Sets the dataIndex for a column.
34933      * @param {Number} col The column index
34934      * @param {Number} dataIndex The new dataIndex
34935      */
34936     setDataIndex : function(col, dataIndex){
34937         this.config[col].dataIndex = dataIndex;
34938     },
34939
34940     
34941     
34942     /**
34943      * Returns true if the cell is editable.
34944      * @param {Number} colIndex The column index
34945      * @param {Number} rowIndex The row index
34946      * @return {Boolean}
34947      */
34948     isCellEditable : function(colIndex, rowIndex){
34949         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
34950     },
34951
34952     /**
34953      * Returns the editor defined for the cell/column.
34954      * return false or null to disable editing.
34955      * @param {Number} colIndex The column index
34956      * @param {Number} rowIndex The row index
34957      * @return {Object}
34958      */
34959     getCellEditor : function(colIndex, rowIndex){
34960         return this.config[colIndex].editor;
34961     },
34962
34963     /**
34964      * Sets if a column is editable.
34965      * @param {Number} col The column index
34966      * @param {Boolean} editable True if the column is editable
34967      */
34968     setEditable : function(col, editable){
34969         this.config[col].editable = editable;
34970     },
34971
34972
34973     /**
34974      * Returns true if the column is hidden.
34975      * @param {Number} colIndex The column index
34976      * @return {Boolean}
34977      */
34978     isHidden : function(colIndex){
34979         return this.config[colIndex].hidden;
34980     },
34981
34982
34983     /**
34984      * Returns true if the column width cannot be changed
34985      */
34986     isFixed : function(colIndex){
34987         return this.config[colIndex].fixed;
34988     },
34989
34990     /**
34991      * Returns true if the column can be resized
34992      * @return {Boolean}
34993      */
34994     isResizable : function(colIndex){
34995         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
34996     },
34997     /**
34998      * Sets if a column is hidden.
34999      * @param {Number} colIndex The column index
35000      * @param {Boolean} hidden True if the column is hidden
35001      */
35002     setHidden : function(colIndex, hidden){
35003         this.config[colIndex].hidden = hidden;
35004         this.totalWidth = null;
35005         this.fireEvent("hiddenchange", this, colIndex, hidden);
35006     },
35007
35008     /**
35009      * Sets the editor for a column.
35010      * @param {Number} col The column index
35011      * @param {Object} editor The editor object
35012      */
35013     setEditor : function(col, editor){
35014         this.config[col].editor = editor;
35015     }
35016 });
35017
35018 Roo.grid.ColumnModel.defaultRenderer = function(value){
35019         if(typeof value == "string" && value.length < 1){
35020             return "&#160;";
35021         }
35022         return value;
35023 };
35024
35025 // Alias for backwards compatibility
35026 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35027 /*
35028  * Based on:
35029  * Ext JS Library 1.1.1
35030  * Copyright(c) 2006-2007, Ext JS, LLC.
35031  *
35032  * Originally Released Under LGPL - original licence link has changed is not relivant.
35033  *
35034  * Fork - LGPL
35035  * <script type="text/javascript">
35036  */
35037
35038 /**
35039  * @class Roo.grid.AbstractSelectionModel
35040  * @extends Roo.util.Observable
35041  * Abstract base class for grid SelectionModels.  It provides the interface that should be
35042  * implemented by descendant classes.  This class should not be directly instantiated.
35043  * @constructor
35044  */
35045 Roo.grid.AbstractSelectionModel = function(){
35046     this.locked = false;
35047     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35048 };
35049
35050 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
35051     /** @ignore Called by the grid automatically. Do not call directly. */
35052     init : function(grid){
35053         this.grid = grid;
35054         this.initEvents();
35055     },
35056
35057     /**
35058      * Locks the selections.
35059      */
35060     lock : function(){
35061         this.locked = true;
35062     },
35063
35064     /**
35065      * Unlocks the selections.
35066      */
35067     unlock : function(){
35068         this.locked = false;
35069     },
35070
35071     /**
35072      * Returns true if the selections are locked.
35073      * @return {Boolean}
35074      */
35075     isLocked : function(){
35076         return this.locked;
35077     }
35078 });/*
35079  * Based on:
35080  * Ext JS Library 1.1.1
35081  * Copyright(c) 2006-2007, Ext JS, LLC.
35082  *
35083  * Originally Released Under LGPL - original licence link has changed is not relivant.
35084  *
35085  * Fork - LGPL
35086  * <script type="text/javascript">
35087  */
35088 /**
35089  * @extends Roo.grid.AbstractSelectionModel
35090  * @class Roo.grid.RowSelectionModel
35091  * The default SelectionModel used by {@link Roo.grid.Grid}.
35092  * It supports multiple selections and keyboard selection/navigation. 
35093  * @constructor
35094  * @param {Object} config
35095  */
35096 Roo.grid.RowSelectionModel = function(config){
35097     Roo.apply(this, config);
35098     this.selections = new Roo.util.MixedCollection(false, function(o){
35099         return o.id;
35100     });
35101
35102     this.last = false;
35103     this.lastActive = false;
35104
35105     this.addEvents({
35106         /**
35107              * @event selectionchange
35108              * Fires when the selection changes
35109              * @param {SelectionModel} this
35110              */
35111             "selectionchange" : true,
35112         /**
35113              * @event afterselectionchange
35114              * Fires after the selection changes (eg. by key press or clicking)
35115              * @param {SelectionModel} this
35116              */
35117             "afterselectionchange" : true,
35118         /**
35119              * @event beforerowselect
35120              * Fires when a row is selected being selected, return false to cancel.
35121              * @param {SelectionModel} this
35122              * @param {Number} rowIndex The selected index
35123              * @param {Boolean} keepExisting False if other selections will be cleared
35124              */
35125             "beforerowselect" : true,
35126         /**
35127              * @event rowselect
35128              * Fires when a row is selected.
35129              * @param {SelectionModel} this
35130              * @param {Number} rowIndex The selected index
35131              * @param {Roo.data.Record} r The record
35132              */
35133             "rowselect" : true,
35134         /**
35135              * @event rowdeselect
35136              * Fires when a row is deselected.
35137              * @param {SelectionModel} this
35138              * @param {Number} rowIndex The selected index
35139              */
35140         "rowdeselect" : true
35141     });
35142     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
35143     this.locked = false;
35144 };
35145
35146 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
35147     /**
35148      * @cfg {Boolean} singleSelect
35149      * True to allow selection of only one row at a time (defaults to false)
35150      */
35151     singleSelect : false,
35152
35153     // private
35154     initEvents : function(){
35155
35156         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
35157             this.grid.on("mousedown", this.handleMouseDown, this);
35158         }else{ // allow click to work like normal
35159             this.grid.on("rowclick", this.handleDragableRowClick, this);
35160         }
35161
35162         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
35163             "up" : function(e){
35164                 if(!e.shiftKey){
35165                     this.selectPrevious(e.shiftKey);
35166                 }else if(this.last !== false && this.lastActive !== false){
35167                     var last = this.last;
35168                     this.selectRange(this.last,  this.lastActive-1);
35169                     this.grid.getView().focusRow(this.lastActive);
35170                     if(last !== false){
35171                         this.last = last;
35172                     }
35173                 }else{
35174                     this.selectFirstRow();
35175                 }
35176                 this.fireEvent("afterselectionchange", this);
35177             },
35178             "down" : function(e){
35179                 if(!e.shiftKey){
35180                     this.selectNext(e.shiftKey);
35181                 }else if(this.last !== false && this.lastActive !== false){
35182                     var last = this.last;
35183                     this.selectRange(this.last,  this.lastActive+1);
35184                     this.grid.getView().focusRow(this.lastActive);
35185                     if(last !== false){
35186                         this.last = last;
35187                     }
35188                 }else{
35189                     this.selectFirstRow();
35190                 }
35191                 this.fireEvent("afterselectionchange", this);
35192             },
35193             scope: this
35194         });
35195
35196         var view = this.grid.view;
35197         view.on("refresh", this.onRefresh, this);
35198         view.on("rowupdated", this.onRowUpdated, this);
35199         view.on("rowremoved", this.onRemove, this);
35200     },
35201
35202     // private
35203     onRefresh : function(){
35204         var ds = this.grid.dataSource, i, v = this.grid.view;
35205         var s = this.selections;
35206         s.each(function(r){
35207             if((i = ds.indexOfId(r.id)) != -1){
35208                 v.onRowSelect(i);
35209             }else{
35210                 s.remove(r);
35211             }
35212         });
35213     },
35214
35215     // private
35216     onRemove : function(v, index, r){
35217         this.selections.remove(r);
35218     },
35219
35220     // private
35221     onRowUpdated : function(v, index, r){
35222         if(this.isSelected(r)){
35223             v.onRowSelect(index);
35224         }
35225     },
35226
35227     /**
35228      * Select records.
35229      * @param {Array} records The records to select
35230      * @param {Boolean} keepExisting (optional) True to keep existing selections
35231      */
35232     selectRecords : function(records, keepExisting){
35233         if(!keepExisting){
35234             this.clearSelections();
35235         }
35236         var ds = this.grid.dataSource;
35237         for(var i = 0, len = records.length; i < len; i++){
35238             this.selectRow(ds.indexOf(records[i]), true);
35239         }
35240     },
35241
35242     /**
35243      * Gets the number of selected rows.
35244      * @return {Number}
35245      */
35246     getCount : function(){
35247         return this.selections.length;
35248     },
35249
35250     /**
35251      * Selects the first row in the grid.
35252      */
35253     selectFirstRow : function(){
35254         this.selectRow(0);
35255     },
35256
35257     /**
35258      * Select the last row.
35259      * @param {Boolean} keepExisting (optional) True to keep existing selections
35260      */
35261     selectLastRow : function(keepExisting){
35262         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
35263     },
35264
35265     /**
35266      * Selects the row immediately following the last selected row.
35267      * @param {Boolean} keepExisting (optional) True to keep existing selections
35268      */
35269     selectNext : function(keepExisting){
35270         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
35271             this.selectRow(this.last+1, keepExisting);
35272             this.grid.getView().focusRow(this.last);
35273         }
35274     },
35275
35276     /**
35277      * Selects the row that precedes the last selected row.
35278      * @param {Boolean} keepExisting (optional) True to keep existing selections
35279      */
35280     selectPrevious : function(keepExisting){
35281         if(this.last){
35282             this.selectRow(this.last-1, keepExisting);
35283             this.grid.getView().focusRow(this.last);
35284         }
35285     },
35286
35287     /**
35288      * Returns the selected records
35289      * @return {Array} Array of selected records
35290      */
35291     getSelections : function(){
35292         return [].concat(this.selections.items);
35293     },
35294
35295     /**
35296      * Returns the first selected record.
35297      * @return {Record}
35298      */
35299     getSelected : function(){
35300         return this.selections.itemAt(0);
35301     },
35302
35303
35304     /**
35305      * Clears all selections.
35306      */
35307     clearSelections : function(fast){
35308         if(this.locked) return;
35309         if(fast !== true){
35310             var ds = this.grid.dataSource;
35311             var s = this.selections;
35312             s.each(function(r){
35313                 this.deselectRow(ds.indexOfId(r.id));
35314             }, this);
35315             s.clear();
35316         }else{
35317             this.selections.clear();
35318         }
35319         this.last = false;
35320     },
35321
35322
35323     /**
35324      * Selects all rows.
35325      */
35326     selectAll : function(){
35327         if(this.locked) return;
35328         this.selections.clear();
35329         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
35330             this.selectRow(i, true);
35331         }
35332     },
35333
35334     /**
35335      * Returns True if there is a selection.
35336      * @return {Boolean}
35337      */
35338     hasSelection : function(){
35339         return this.selections.length > 0;
35340     },
35341
35342     /**
35343      * Returns True if the specified row is selected.
35344      * @param {Number/Record} record The record or index of the record to check
35345      * @return {Boolean}
35346      */
35347     isSelected : function(index){
35348         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
35349         return (r && this.selections.key(r.id) ? true : false);
35350     },
35351
35352     /**
35353      * Returns True if the specified record id is selected.
35354      * @param {String} id The id of record to check
35355      * @return {Boolean}
35356      */
35357     isIdSelected : function(id){
35358         return (this.selections.key(id) ? true : false);
35359     },
35360
35361     // private
35362     handleMouseDown : function(e, t){
35363         var view = this.grid.getView(), rowIndex;
35364         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
35365             return;
35366         };
35367         if(e.shiftKey && this.last !== false){
35368             var last = this.last;
35369             this.selectRange(last, rowIndex, e.ctrlKey);
35370             this.last = last; // reset the last
35371             view.focusRow(rowIndex);
35372         }else{
35373             var isSelected = this.isSelected(rowIndex);
35374             if(e.button !== 0 && isSelected){
35375                 view.focusRow(rowIndex);
35376             }else if(e.ctrlKey && isSelected){
35377                 this.deselectRow(rowIndex);
35378             }else if(!isSelected){
35379                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
35380                 view.focusRow(rowIndex);
35381             }
35382         }
35383         this.fireEvent("afterselectionchange", this);
35384     },
35385     // private
35386     handleDragableRowClick :  function(grid, rowIndex, e) 
35387     {
35388         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
35389             this.selectRow(rowIndex, false);
35390             grid.view.focusRow(rowIndex);
35391              this.fireEvent("afterselectionchange", this);
35392         }
35393     },
35394     
35395     /**
35396      * Selects multiple rows.
35397      * @param {Array} rows Array of the indexes of the row to select
35398      * @param {Boolean} keepExisting (optional) True to keep existing selections
35399      */
35400     selectRows : function(rows, keepExisting){
35401         if(!keepExisting){
35402             this.clearSelections();
35403         }
35404         for(var i = 0, len = rows.length; i < len; i++){
35405             this.selectRow(rows[i], true);
35406         }
35407     },
35408
35409     /**
35410      * Selects a range of rows. All rows in between startRow and endRow are also selected.
35411      * @param {Number} startRow The index of the first row in the range
35412      * @param {Number} endRow The index of the last row in the range
35413      * @param {Boolean} keepExisting (optional) True to retain existing selections
35414      */
35415     selectRange : function(startRow, endRow, keepExisting){
35416         if(this.locked) return;
35417         if(!keepExisting){
35418             this.clearSelections();
35419         }
35420         if(startRow <= endRow){
35421             for(var i = startRow; i <= endRow; i++){
35422                 this.selectRow(i, true);
35423             }
35424         }else{
35425             for(var i = startRow; i >= endRow; i--){
35426                 this.selectRow(i, true);
35427             }
35428         }
35429     },
35430
35431     /**
35432      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
35433      * @param {Number} startRow The index of the first row in the range
35434      * @param {Number} endRow The index of the last row in the range
35435      */
35436     deselectRange : function(startRow, endRow, preventViewNotify){
35437         if(this.locked) return;
35438         for(var i = startRow; i <= endRow; i++){
35439             this.deselectRow(i, preventViewNotify);
35440         }
35441     },
35442
35443     /**
35444      * Selects a row.
35445      * @param {Number} row The index of the row to select
35446      * @param {Boolean} keepExisting (optional) True to keep existing selections
35447      */
35448     selectRow : function(index, keepExisting, preventViewNotify){
35449         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
35450         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
35451             if(!keepExisting || this.singleSelect){
35452                 this.clearSelections();
35453             }
35454             var r = this.grid.dataSource.getAt(index);
35455             this.selections.add(r);
35456             this.last = this.lastActive = index;
35457             if(!preventViewNotify){
35458                 this.grid.getView().onRowSelect(index);
35459             }
35460             this.fireEvent("rowselect", this, index, r);
35461             this.fireEvent("selectionchange", this);
35462         }
35463     },
35464
35465     /**
35466      * Deselects a row.
35467      * @param {Number} row The index of the row to deselect
35468      */
35469     deselectRow : function(index, preventViewNotify){
35470         if(this.locked) return;
35471         if(this.last == index){
35472             this.last = false;
35473         }
35474         if(this.lastActive == index){
35475             this.lastActive = false;
35476         }
35477         var r = this.grid.dataSource.getAt(index);
35478         this.selections.remove(r);
35479         if(!preventViewNotify){
35480             this.grid.getView().onRowDeselect(index);
35481         }
35482         this.fireEvent("rowdeselect", this, index);
35483         this.fireEvent("selectionchange", this);
35484     },
35485
35486     // private
35487     restoreLast : function(){
35488         if(this._last){
35489             this.last = this._last;
35490         }
35491     },
35492
35493     // private
35494     acceptsNav : function(row, col, cm){
35495         return !cm.isHidden(col) && cm.isCellEditable(col, row);
35496     },
35497
35498     // private
35499     onEditorKey : function(field, e){
35500         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
35501         if(k == e.TAB){
35502             e.stopEvent();
35503             ed.completeEdit();
35504             if(e.shiftKey){
35505                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35506             }else{
35507                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35508             }
35509         }else if(k == e.ENTER && !e.ctrlKey){
35510             e.stopEvent();
35511             ed.completeEdit();
35512             if(e.shiftKey){
35513                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
35514             }else{
35515                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
35516             }
35517         }else if(k == e.ESC){
35518             ed.cancelEdit();
35519         }
35520         if(newCell){
35521             g.startEditing(newCell[0], newCell[1]);
35522         }
35523     }
35524 });/*
35525  * Based on:
35526  * Ext JS Library 1.1.1
35527  * Copyright(c) 2006-2007, Ext JS, LLC.
35528  *
35529  * Originally Released Under LGPL - original licence link has changed is not relivant.
35530  *
35531  * Fork - LGPL
35532  * <script type="text/javascript">
35533  */
35534 /**
35535  * @class Roo.grid.CellSelectionModel
35536  * @extends Roo.grid.AbstractSelectionModel
35537  * This class provides the basic implementation for cell selection in a grid.
35538  * @constructor
35539  * @param {Object} config The object containing the configuration of this model.
35540  */
35541 Roo.grid.CellSelectionModel = function(config){
35542     Roo.apply(this, config);
35543
35544     this.selection = null;
35545
35546     this.addEvents({
35547         /**
35548              * @event beforerowselect
35549              * Fires before a cell is selected.
35550              * @param {SelectionModel} this
35551              * @param {Number} rowIndex The selected row index
35552              * @param {Number} colIndex The selected cell index
35553              */
35554             "beforecellselect" : true,
35555         /**
35556              * @event cellselect
35557              * Fires when a cell is selected.
35558              * @param {SelectionModel} this
35559              * @param {Number} rowIndex The selected row index
35560              * @param {Number} colIndex The selected cell index
35561              */
35562             "cellselect" : true,
35563         /**
35564              * @event selectionchange
35565              * Fires when the active selection changes.
35566              * @param {SelectionModel} this
35567              * @param {Object} selection null for no selection or an object (o) with two properties
35568                 <ul>
35569                 <li>o.record: the record object for the row the selection is in</li>
35570                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
35571                 </ul>
35572              */
35573             "selectionchange" : true
35574     });
35575     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
35576 };
35577
35578 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
35579
35580     /** @ignore */
35581     initEvents : function(){
35582         this.grid.on("mousedown", this.handleMouseDown, this);
35583         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
35584         var view = this.grid.view;
35585         view.on("refresh", this.onViewChange, this);
35586         view.on("rowupdated", this.onRowUpdated, this);
35587         view.on("beforerowremoved", this.clearSelections, this);
35588         view.on("beforerowsinserted", this.clearSelections, this);
35589         if(this.grid.isEditor){
35590             this.grid.on("beforeedit", this.beforeEdit,  this);
35591         }
35592     },
35593
35594         //private
35595     beforeEdit : function(e){
35596         this.select(e.row, e.column, false, true, e.record);
35597     },
35598
35599         //private
35600     onRowUpdated : function(v, index, r){
35601         if(this.selection && this.selection.record == r){
35602             v.onCellSelect(index, this.selection.cell[1]);
35603         }
35604     },
35605
35606         //private
35607     onViewChange : function(){
35608         this.clearSelections(true);
35609     },
35610
35611         /**
35612          * Returns the currently selected cell,.
35613          * @return {Array} The selected cell (row, column) or null if none selected.
35614          */
35615     getSelectedCell : function(){
35616         return this.selection ? this.selection.cell : null;
35617     },
35618
35619     /**
35620      * Clears all selections.
35621      * @param {Boolean} true to prevent the gridview from being notified about the change.
35622      */
35623     clearSelections : function(preventNotify){
35624         var s = this.selection;
35625         if(s){
35626             if(preventNotify !== true){
35627                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
35628             }
35629             this.selection = null;
35630             this.fireEvent("selectionchange", this, null);
35631         }
35632     },
35633
35634     /**
35635      * Returns true if there is a selection.
35636      * @return {Boolean}
35637      */
35638     hasSelection : function(){
35639         return this.selection ? true : false;
35640     },
35641
35642     /** @ignore */
35643     handleMouseDown : function(e, t){
35644         var v = this.grid.getView();
35645         if(this.isLocked()){
35646             return;
35647         };
35648         var row = v.findRowIndex(t);
35649         var cell = v.findCellIndex(t);
35650         if(row !== false && cell !== false){
35651             this.select(row, cell);
35652         }
35653     },
35654
35655     /**
35656      * Selects a cell.
35657      * @param {Number} rowIndex
35658      * @param {Number} collIndex
35659      */
35660     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
35661         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
35662             this.clearSelections();
35663             r = r || this.grid.dataSource.getAt(rowIndex);
35664             this.selection = {
35665                 record : r,
35666                 cell : [rowIndex, colIndex]
35667             };
35668             if(!preventViewNotify){
35669                 var v = this.grid.getView();
35670                 v.onCellSelect(rowIndex, colIndex);
35671                 if(preventFocus !== true){
35672                     v.focusCell(rowIndex, colIndex);
35673                 }
35674             }
35675             this.fireEvent("cellselect", this, rowIndex, colIndex);
35676             this.fireEvent("selectionchange", this, this.selection);
35677         }
35678     },
35679
35680         //private
35681     isSelectable : function(rowIndex, colIndex, cm){
35682         return !cm.isHidden(colIndex);
35683     },
35684
35685     /** @ignore */
35686     handleKeyDown : function(e){
35687         Roo.log('Cell Sel Model handleKeyDown');
35688         if(!e.isNavKeyPress()){
35689             return;
35690         }
35691         var g = this.grid, s = this.selection;
35692         if(!s){
35693             e.stopEvent();
35694             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
35695             if(cell){
35696                 this.select(cell[0], cell[1]);
35697             }
35698             return;
35699         }
35700         var sm = this;
35701         var walk = function(row, col, step){
35702             return g.walkCells(row, col, step, sm.isSelectable,  sm);
35703         };
35704         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
35705         var newCell;
35706
35707         switch(k){
35708             case e.TAB:
35709                 // handled by onEditorKey
35710                 if (g.isEditor && g.editing) {
35711                     return;
35712                 }
35713                 if(e.shiftKey){
35714                      newCell = walk(r, c-1, -1);
35715                 }else{
35716                      newCell = walk(r, c+1, 1);
35717                 }
35718              break;
35719              case e.DOWN:
35720                  newCell = walk(r+1, c, 1);
35721              break;
35722              case e.UP:
35723                  newCell = walk(r-1, c, -1);
35724              break;
35725              case e.RIGHT:
35726                  newCell = walk(r, c+1, 1);
35727              break;
35728              case e.LEFT:
35729                  newCell = walk(r, c-1, -1);
35730              break;
35731              case e.ENTER:
35732                  if(g.isEditor && !g.editing){
35733                     g.startEditing(r, c);
35734                     e.stopEvent();
35735                     return;
35736                 }
35737              break;
35738         };
35739         if(newCell){
35740             this.select(newCell[0], newCell[1]);
35741             e.stopEvent();
35742         }
35743     },
35744
35745     acceptsNav : function(row, col, cm){
35746         return !cm.isHidden(col) && cm.isCellEditable(col, row);
35747     },
35748
35749     onEditorKey : function(field, e){
35750         
35751         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
35752         ///Roo.log('onEditorKey' + k);
35753         
35754         if(k == e.TAB){
35755             if(e.shiftKey){
35756                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35757             }else{
35758                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35759             }
35760             e.stopEvent();
35761         }else if(k == e.ENTER && !e.ctrlKey){
35762             ed.completeEdit();
35763             e.stopEvent();
35764             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35765         }else if(k == e.ESC){
35766             ed.cancelEdit();
35767         }
35768         
35769         
35770         if(newCell){
35771             //Roo.log('next cell after edit');
35772             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
35773         }
35774     }
35775 });/*
35776  * Based on:
35777  * Ext JS Library 1.1.1
35778  * Copyright(c) 2006-2007, Ext JS, LLC.
35779  *
35780  * Originally Released Under LGPL - original licence link has changed is not relivant.
35781  *
35782  * Fork - LGPL
35783  * <script type="text/javascript">
35784  */
35785  
35786 /**
35787  * @class Roo.grid.EditorGrid
35788  * @extends Roo.grid.Grid
35789  * Class for creating and editable grid.
35790  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
35791  * The container MUST have some type of size defined for the grid to fill. The container will be 
35792  * automatically set to position relative if it isn't already.
35793  * @param {Object} dataSource The data model to bind to
35794  * @param {Object} colModel The column model with info about this grid's columns
35795  */
35796 Roo.grid.EditorGrid = function(container, config){
35797     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
35798     this.getGridEl().addClass("xedit-grid");
35799
35800     if(!this.selModel){
35801         this.selModel = new Roo.grid.CellSelectionModel();
35802     }
35803
35804     this.activeEditor = null;
35805
35806         this.addEvents({
35807             /**
35808              * @event beforeedit
35809              * Fires before cell editing is triggered. The edit event object has the following properties <br />
35810              * <ul style="padding:5px;padding-left:16px;">
35811              * <li>grid - This grid</li>
35812              * <li>record - The record being edited</li>
35813              * <li>field - The field name being edited</li>
35814              * <li>value - The value for the field being edited.</li>
35815              * <li>row - The grid row index</li>
35816              * <li>column - The grid column index</li>
35817              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
35818              * </ul>
35819              * @param {Object} e An edit event (see above for description)
35820              */
35821             "beforeedit" : true,
35822             /**
35823              * @event afteredit
35824              * Fires after a cell is edited. <br />
35825              * <ul style="padding:5px;padding-left:16px;">
35826              * <li>grid - This grid</li>
35827              * <li>record - The record being edited</li>
35828              * <li>field - The field name being edited</li>
35829              * <li>value - The value being set</li>
35830              * <li>originalValue - The original value for the field, before the edit.</li>
35831              * <li>row - The grid row index</li>
35832              * <li>column - The grid column index</li>
35833              * </ul>
35834              * @param {Object} e An edit event (see above for description)
35835              */
35836             "afteredit" : true,
35837             /**
35838              * @event validateedit
35839              * Fires after a cell is edited, but before the value is set in the record. 
35840          * You can use this to modify the value being set in the field, Return false
35841              * to cancel the change. The edit event object has the following properties <br />
35842              * <ul style="padding:5px;padding-left:16px;">
35843          * <li>editor - This editor</li>
35844              * <li>grid - This grid</li>
35845              * <li>record - The record being edited</li>
35846              * <li>field - The field name being edited</li>
35847              * <li>value - The value being set</li>
35848              * <li>originalValue - The original value for the field, before the edit.</li>
35849              * <li>row - The grid row index</li>
35850              * <li>column - The grid column index</li>
35851              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
35852              * </ul>
35853              * @param {Object} e An edit event (see above for description)
35854              */
35855             "validateedit" : true
35856         });
35857     this.on("bodyscroll", this.stopEditing,  this);
35858     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
35859 };
35860
35861 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
35862     /**
35863      * @cfg {Number} clicksToEdit
35864      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
35865      */
35866     clicksToEdit: 2,
35867
35868     // private
35869     isEditor : true,
35870     // private
35871     trackMouseOver: false, // causes very odd FF errors
35872
35873     onCellDblClick : function(g, row, col){
35874         this.startEditing(row, col);
35875     },
35876
35877     onEditComplete : function(ed, value, startValue){
35878         this.editing = false;
35879         this.activeEditor = null;
35880         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
35881         var r = ed.record;
35882         var field = this.colModel.getDataIndex(ed.col);
35883         var e = {
35884             grid: this,
35885             record: r,
35886             field: field,
35887             originalValue: startValue,
35888             value: value,
35889             row: ed.row,
35890             column: ed.col,
35891             cancel:false,
35892             editor: ed
35893         };
35894         if(String(value) !== String(startValue)){
35895             
35896             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
35897                 r.set(field, e.value);
35898                 // if we are dealing with a combo box..
35899                 // then we also set the 'name' colum to be the displayField
35900                 if (ed.field.displayField && ed.field.name) {
35901                     r.set(ed.field.name, ed.field.el.dom.value);
35902                 }
35903                 
35904                 delete e.cancel; //?? why!!!
35905                 this.fireEvent("afteredit", e);
35906             }
35907         } else {
35908             this.fireEvent("afteredit", e); // always fire it!
35909         }
35910         this.view.focusCell(ed.row, ed.col);
35911     },
35912
35913     /**
35914      * Starts editing the specified for the specified row/column
35915      * @param {Number} rowIndex
35916      * @param {Number} colIndex
35917      */
35918     startEditing : function(row, col){
35919         this.stopEditing();
35920         if(this.colModel.isCellEditable(col, row)){
35921             this.view.ensureVisible(row, col, true);
35922             var r = this.dataSource.getAt(row);
35923             var field = this.colModel.getDataIndex(col);
35924             var e = {
35925                 grid: this,
35926                 record: r,
35927                 field: field,
35928                 value: r.data[field],
35929                 row: row,
35930                 column: col,
35931                 cancel:false
35932             };
35933             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
35934                 this.editing = true;
35935                 var ed = this.colModel.getCellEditor(col, row);
35936                 
35937                 if (!ed) {
35938                     return;
35939                 }
35940                 if(!ed.rendered){
35941                     ed.render(ed.parentEl || document.body);
35942                 }
35943                 ed.field.reset();
35944                 (function(){ // complex but required for focus issues in safari, ie and opera
35945                     ed.row = row;
35946                     ed.col = col;
35947                     ed.record = r;
35948                     ed.on("complete", this.onEditComplete, this, {single: true});
35949                     ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
35950                     this.activeEditor = ed;
35951                     var v = r.data[field];
35952                     ed.startEdit(this.view.getCell(row, col), v);
35953                     // combo's with 'displayField and name set
35954                     if (ed.field.displayField && ed.field.name) {
35955                         ed.field.el.dom.value = r.data[ed.field.name];
35956                     }
35957                     
35958                     
35959                 }).defer(50, this);
35960             }
35961         }
35962     },
35963         
35964     /**
35965      * Stops any active editing
35966      */
35967     stopEditing : function(){
35968         if(this.activeEditor){
35969             this.activeEditor.completeEdit();
35970         }
35971         this.activeEditor = null;
35972     }
35973 });/*
35974  * Based on:
35975  * Ext JS Library 1.1.1
35976  * Copyright(c) 2006-2007, Ext JS, LLC.
35977  *
35978  * Originally Released Under LGPL - original licence link has changed is not relivant.
35979  *
35980  * Fork - LGPL
35981  * <script type="text/javascript">
35982  */
35983
35984 // private - not really -- you end up using it !
35985 // This is a support class used internally by the Grid components
35986
35987 /**
35988  * @class Roo.grid.GridEditor
35989  * @extends Roo.Editor
35990  * Class for creating and editable grid elements.
35991  * @param {Object} config any settings (must include field)
35992  */
35993 Roo.grid.GridEditor = function(field, config){
35994     if (!config && field.field) {
35995         config = field;
35996         field = Roo.factory(config.field, Roo.form);
35997     }
35998     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
35999     field.monitorTab = false;
36000 };
36001
36002 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36003     
36004     /**
36005      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36006      */
36007     
36008     alignment: "tl-tl",
36009     autoSize: "width",
36010     hideEl : false,
36011     cls: "x-small-editor x-grid-editor",
36012     shim:false,
36013     shadow:"frame"
36014 });/*
36015  * Based on:
36016  * Ext JS Library 1.1.1
36017  * Copyright(c) 2006-2007, Ext JS, LLC.
36018  *
36019  * Originally Released Under LGPL - original licence link has changed is not relivant.
36020  *
36021  * Fork - LGPL
36022  * <script type="text/javascript">
36023  */
36024   
36025
36026   
36027 Roo.grid.PropertyRecord = Roo.data.Record.create([
36028     {name:'name',type:'string'},  'value'
36029 ]);
36030
36031
36032 Roo.grid.PropertyStore = function(grid, source){
36033     this.grid = grid;
36034     this.store = new Roo.data.Store({
36035         recordType : Roo.grid.PropertyRecord
36036     });
36037     this.store.on('update', this.onUpdate,  this);
36038     if(source){
36039         this.setSource(source);
36040     }
36041     Roo.grid.PropertyStore.superclass.constructor.call(this);
36042 };
36043
36044
36045
36046 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
36047     setSource : function(o){
36048         this.source = o;
36049         this.store.removeAll();
36050         var data = [];
36051         for(var k in o){
36052             if(this.isEditableValue(o[k])){
36053                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
36054             }
36055         }
36056         this.store.loadRecords({records: data}, {}, true);
36057     },
36058
36059     onUpdate : function(ds, record, type){
36060         if(type == Roo.data.Record.EDIT){
36061             var v = record.data['value'];
36062             var oldValue = record.modified['value'];
36063             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
36064                 this.source[record.id] = v;
36065                 record.commit();
36066                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
36067             }else{
36068                 record.reject();
36069             }
36070         }
36071     },
36072
36073     getProperty : function(row){
36074        return this.store.getAt(row);
36075     },
36076
36077     isEditableValue: function(val){
36078         if(val && val instanceof Date){
36079             return true;
36080         }else if(typeof val == 'object' || typeof val == 'function'){
36081             return false;
36082         }
36083         return true;
36084     },
36085
36086     setValue : function(prop, value){
36087         this.source[prop] = value;
36088         this.store.getById(prop).set('value', value);
36089     },
36090
36091     getSource : function(){
36092         return this.source;
36093     }
36094 });
36095
36096 Roo.grid.PropertyColumnModel = function(grid, store){
36097     this.grid = grid;
36098     var g = Roo.grid;
36099     g.PropertyColumnModel.superclass.constructor.call(this, [
36100         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
36101         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
36102     ]);
36103     this.store = store;
36104     this.bselect = Roo.DomHelper.append(document.body, {
36105         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
36106             {tag: 'option', value: 'true', html: 'true'},
36107             {tag: 'option', value: 'false', html: 'false'}
36108         ]
36109     });
36110     Roo.id(this.bselect);
36111     var f = Roo.form;
36112     this.editors = {
36113         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
36114         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
36115         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
36116         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
36117         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
36118     };
36119     this.renderCellDelegate = this.renderCell.createDelegate(this);
36120     this.renderPropDelegate = this.renderProp.createDelegate(this);
36121 };
36122
36123 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
36124     
36125     
36126     nameText : 'Name',
36127     valueText : 'Value',
36128     
36129     dateFormat : 'm/j/Y',
36130     
36131     
36132     renderDate : function(dateVal){
36133         return dateVal.dateFormat(this.dateFormat);
36134     },
36135
36136     renderBool : function(bVal){
36137         return bVal ? 'true' : 'false';
36138     },
36139
36140     isCellEditable : function(colIndex, rowIndex){
36141         return colIndex == 1;
36142     },
36143
36144     getRenderer : function(col){
36145         return col == 1 ?
36146             this.renderCellDelegate : this.renderPropDelegate;
36147     },
36148
36149     renderProp : function(v){
36150         return this.getPropertyName(v);
36151     },
36152
36153     renderCell : function(val){
36154         var rv = val;
36155         if(val instanceof Date){
36156             rv = this.renderDate(val);
36157         }else if(typeof val == 'boolean'){
36158             rv = this.renderBool(val);
36159         }
36160         return Roo.util.Format.htmlEncode(rv);
36161     },
36162
36163     getPropertyName : function(name){
36164         var pn = this.grid.propertyNames;
36165         return pn && pn[name] ? pn[name] : name;
36166     },
36167
36168     getCellEditor : function(colIndex, rowIndex){
36169         var p = this.store.getProperty(rowIndex);
36170         var n = p.data['name'], val = p.data['value'];
36171         
36172         if(typeof(this.grid.customEditors[n]) == 'string'){
36173             return this.editors[this.grid.customEditors[n]];
36174         }
36175         if(typeof(this.grid.customEditors[n]) != 'undefined'){
36176             return this.grid.customEditors[n];
36177         }
36178         if(val instanceof Date){
36179             return this.editors['date'];
36180         }else if(typeof val == 'number'){
36181             return this.editors['number'];
36182         }else if(typeof val == 'boolean'){
36183             return this.editors['boolean'];
36184         }else{
36185             return this.editors['string'];
36186         }
36187     }
36188 });
36189
36190 /**
36191  * @class Roo.grid.PropertyGrid
36192  * @extends Roo.grid.EditorGrid
36193  * This class represents the  interface of a component based property grid control.
36194  * <br><br>Usage:<pre><code>
36195  var grid = new Roo.grid.PropertyGrid("my-container-id", {
36196       
36197  });
36198  // set any options
36199  grid.render();
36200  * </code></pre>
36201   
36202  * @constructor
36203  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36204  * The container MUST have some type of size defined for the grid to fill. The container will be
36205  * automatically set to position relative if it isn't already.
36206  * @param {Object} config A config object that sets properties on this grid.
36207  */
36208 Roo.grid.PropertyGrid = function(container, config){
36209     config = config || {};
36210     var store = new Roo.grid.PropertyStore(this);
36211     this.store = store;
36212     var cm = new Roo.grid.PropertyColumnModel(this, store);
36213     store.store.sort('name', 'ASC');
36214     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
36215         ds: store.store,
36216         cm: cm,
36217         enableColLock:false,
36218         enableColumnMove:false,
36219         stripeRows:false,
36220         trackMouseOver: false,
36221         clicksToEdit:1
36222     }, config));
36223     this.getGridEl().addClass('x-props-grid');
36224     this.lastEditRow = null;
36225     this.on('columnresize', this.onColumnResize, this);
36226     this.addEvents({
36227          /**
36228              * @event beforepropertychange
36229              * Fires before a property changes (return false to stop?)
36230              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36231              * @param {String} id Record Id
36232              * @param {String} newval New Value
36233          * @param {String} oldval Old Value
36234              */
36235         "beforepropertychange": true,
36236         /**
36237              * @event propertychange
36238              * Fires after a property changes
36239              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36240              * @param {String} id Record Id
36241              * @param {String} newval New Value
36242          * @param {String} oldval Old Value
36243              */
36244         "propertychange": true
36245     });
36246     this.customEditors = this.customEditors || {};
36247 };
36248 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
36249     
36250      /**
36251      * @cfg {Object} customEditors map of colnames=> custom editors.
36252      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
36253      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
36254      * false disables editing of the field.
36255          */
36256     
36257       /**
36258      * @cfg {Object} propertyNames map of property Names to their displayed value
36259          */
36260     
36261     render : function(){
36262         Roo.grid.PropertyGrid.superclass.render.call(this);
36263         this.autoSize.defer(100, this);
36264     },
36265
36266     autoSize : function(){
36267         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
36268         if(this.view){
36269             this.view.fitColumns();
36270         }
36271     },
36272
36273     onColumnResize : function(){
36274         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
36275         this.autoSize();
36276     },
36277     /**
36278      * Sets the data for the Grid
36279      * accepts a Key => Value object of all the elements avaiable.
36280      * @param {Object} data  to appear in grid.
36281      */
36282     setSource : function(source){
36283         this.store.setSource(source);
36284         //this.autoSize();
36285     },
36286     /**
36287      * Gets all the data from the grid.
36288      * @return {Object} data  data stored in grid
36289      */
36290     getSource : function(){
36291         return this.store.getSource();
36292     }
36293 });/*
36294  * Based on:
36295  * Ext JS Library 1.1.1
36296  * Copyright(c) 2006-2007, Ext JS, LLC.
36297  *
36298  * Originally Released Under LGPL - original licence link has changed is not relivant.
36299  *
36300  * Fork - LGPL
36301  * <script type="text/javascript">
36302  */
36303  
36304 /**
36305  * @class Roo.LoadMask
36306  * A simple utility class for generically masking elements while loading data.  If the element being masked has
36307  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
36308  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
36309  * element's UpdateManager load indicator and will be destroyed after the initial load.
36310  * @constructor
36311  * Create a new LoadMask
36312  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
36313  * @param {Object} config The config object
36314  */
36315 Roo.LoadMask = function(el, config){
36316     this.el = Roo.get(el);
36317     Roo.apply(this, config);
36318     if(this.store){
36319         this.store.on('beforeload', this.onBeforeLoad, this);
36320         this.store.on('load', this.onLoad, this);
36321         this.store.on('loadexception', this.onLoad, this);
36322         this.removeMask = false;
36323     }else{
36324         var um = this.el.getUpdateManager();
36325         um.showLoadIndicator = false; // disable the default indicator
36326         um.on('beforeupdate', this.onBeforeLoad, this);
36327         um.on('update', this.onLoad, this);
36328         um.on('failure', this.onLoad, this);
36329         this.removeMask = true;
36330     }
36331 };
36332
36333 Roo.LoadMask.prototype = {
36334     /**
36335      * @cfg {Boolean} removeMask
36336      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
36337      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
36338      */
36339     /**
36340      * @cfg {String} msg
36341      * The text to display in a centered loading message box (defaults to 'Loading...')
36342      */
36343     msg : 'Loading...',
36344     /**
36345      * @cfg {String} msgCls
36346      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
36347      */
36348     msgCls : 'x-mask-loading',
36349
36350     /**
36351      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
36352      * @type Boolean
36353      */
36354     disabled: false,
36355
36356     /**
36357      * Disables the mask to prevent it from being displayed
36358      */
36359     disable : function(){
36360        this.disabled = true;
36361     },
36362
36363     /**
36364      * Enables the mask so that it can be displayed
36365      */
36366     enable : function(){
36367         this.disabled = false;
36368     },
36369
36370     // private
36371     onLoad : function(){
36372         this.el.unmask(this.removeMask);
36373     },
36374
36375     // private
36376     onBeforeLoad : function(){
36377         if(!this.disabled){
36378             this.el.mask(this.msg, this.msgCls);
36379         }
36380     },
36381
36382     // private
36383     destroy : function(){
36384         if(this.store){
36385             this.store.un('beforeload', this.onBeforeLoad, this);
36386             this.store.un('load', this.onLoad, this);
36387             this.store.un('loadexception', this.onLoad, this);
36388         }else{
36389             var um = this.el.getUpdateManager();
36390             um.un('beforeupdate', this.onBeforeLoad, this);
36391             um.un('update', this.onLoad, this);
36392             um.un('failure', this.onLoad, this);
36393         }
36394     }
36395 };/*
36396  * Based on:
36397  * Ext JS Library 1.1.1
36398  * Copyright(c) 2006-2007, Ext JS, LLC.
36399  *
36400  * Originally Released Under LGPL - original licence link has changed is not relivant.
36401  *
36402  * Fork - LGPL
36403  * <script type="text/javascript">
36404  */
36405 Roo.XTemplate = function(){
36406     Roo.XTemplate.superclass.constructor.apply(this, arguments);
36407     var s = this.html;
36408
36409     s = ['<tpl>', s, '</tpl>'].join('');
36410
36411     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
36412
36413     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
36414     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
36415     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
36416     var m, id = 0;
36417     var tpls = [];
36418
36419     while(m = s.match(re)){
36420        var m2 = m[0].match(nameRe);
36421        var m3 = m[0].match(ifRe);
36422        var m4 = m[0].match(execRe);
36423        var exp = null, fn = null, exec = null;
36424        var name = m2 && m2[1] ? m2[1] : '';
36425        if(m3){
36426            exp = m3 && m3[1] ? m3[1] : null;
36427            if(exp){
36428                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
36429            }
36430        }
36431        if(m4){
36432            exp = m4 && m4[1] ? m4[1] : null;
36433            if(exp){
36434                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
36435            }
36436        }
36437        if(name){
36438            switch(name){
36439                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
36440                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
36441                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
36442            }
36443        }
36444        tpls.push({
36445             id: id,
36446             target: name,
36447             exec: exec,
36448             test: fn,
36449             body: m[1]||''
36450         });
36451        s = s.replace(m[0], '{xtpl'+ id + '}');
36452        ++id;
36453     }
36454     for(var i = tpls.length-1; i >= 0; --i){
36455         this.compileTpl(tpls[i]);
36456     }
36457     this.master = tpls[tpls.length-1];
36458     this.tpls = tpls;
36459 };
36460 Roo.extend(Roo.XTemplate, Roo.Template, {
36461
36462     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
36463
36464     applySubTemplate : function(id, values, parent){
36465         var t = this.tpls[id];
36466         if(t.test && !t.test.call(this, values, parent)){
36467             return '';
36468         }
36469         if(t.exec && t.exec.call(this, values, parent)){
36470             return '';
36471         }
36472         var vs = t.target ? t.target.call(this, values, parent) : values;
36473         parent = t.target ? values : parent;
36474         if(t.target && vs instanceof Array){
36475             var buf = [];
36476             for(var i = 0, len = vs.length; i < len; i++){
36477                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
36478             }
36479             return buf.join('');
36480         }
36481         return t.compiled.call(this, vs, parent);
36482     },
36483
36484     compileTpl : function(tpl){
36485         var fm = Roo.util.Format;
36486         var useF = this.disableFormats !== true;
36487         var sep = Roo.isGecko ? "+" : ",";
36488         var fn = function(m, name, format, args){
36489             if(name.substr(0, 4) == 'xtpl'){
36490                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
36491             }
36492             var v;
36493             if(name.indexOf('.') != -1){
36494                 v = name;
36495             }else{
36496                 v = "values['" + name + "']";
36497             }
36498             if(format && useF){
36499                 args = args ? ',' + args : "";
36500                 if(format.substr(0, 5) != "this."){
36501                     format = "fm." + format + '(';
36502                 }else{
36503                     format = 'this.call("'+ format.substr(5) + '", ';
36504                     args = ", values";
36505                 }
36506             }else{
36507                 args= ''; format = "("+v+" === undefined ? '' : ";
36508             }
36509             return "'"+ sep + format + v + args + ")"+sep+"'";
36510         };
36511         var body;
36512         // branched to use + in gecko and [].join() in others
36513         if(Roo.isGecko){
36514             body = "tpl.compiled = function(values, parent){ return '" +
36515                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
36516                     "';};";
36517         }else{
36518             body = ["tpl.compiled = function(values, parent){ return ['"];
36519             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
36520             body.push("'].join('');};");
36521             body = body.join('');
36522         }
36523         /** eval:var:zzzzzzz */
36524         eval(body);
36525         return this;
36526     },
36527
36528     applyTemplate : function(values){
36529         return this.master.compiled.call(this, values, {});
36530         var s = this.subs;
36531     },
36532
36533     apply : function(){
36534         return this.applyTemplate.apply(this, arguments);
36535     },
36536
36537     compile : function(){return this;}
36538 });
36539
36540 Roo.XTemplate.from = function(el){
36541     el = Roo.getDom(el);
36542     return new Roo.XTemplate(el.value || el.innerHTML);
36543 };/*
36544  * Original code for Roojs - LGPL
36545  * <script type="text/javascript">
36546  */
36547  
36548 /**
36549  * @class Roo.XComponent
36550  * A delayed Element creator...
36551  * Or a way to group chunks of interface together.
36552  * 
36553  * Mypart.xyx = new Roo.XComponent({
36554
36555     parent : 'Mypart.xyz', // empty == document.element.!!
36556     order : '001',
36557     name : 'xxxx'
36558     region : 'xxxx'
36559     disabled : function() {} 
36560      
36561     tree : function() { // return an tree of xtype declared components
36562         var MODULE = this;
36563         return 
36564         {
36565             xtype : 'NestedLayoutPanel',
36566             // technicall
36567         }
36568      ]
36569  *})
36570  *
36571  *
36572  * It can be used to build a big heiracy, with parent etc.
36573  * or you can just use this to render a single compoent to a dom element
36574  * MYPART.render(Roo.Element | String(id) | dom_element )
36575  * 
36576  * @extends Roo.util.Observable
36577  * @constructor
36578  * @param cfg {Object} configuration of component
36579  * 
36580  */
36581 Roo.XComponent = function(cfg) {
36582     Roo.apply(this, cfg);
36583     this.addEvents({ 
36584         /**
36585              * @event built
36586              * Fires when this the componnt is built
36587              * @param {Roo.XComponent} c the component
36588              */
36589         'built' : true,
36590         /**
36591              * @event buildcomplete
36592              * Fires on the top level element when all elements have been built
36593              * @param {Roo.XComponent} c the top level component.
36594          */
36595         'buildcomplete' : true
36596         
36597     });
36598     
36599     Roo.XComponent.register(this);
36600     this.modules = false;
36601     this.el = false; // where the layout goes..
36602     
36603     
36604 }
36605 Roo.extend(Roo.XComponent, Roo.util.Observable, {
36606     /**
36607      * @property el
36608      * The created element (with Roo.factory())
36609      * @type {Roo.Layout}
36610      */
36611     el  : false,
36612     
36613     /**
36614      * @property el
36615      * for BC  - use el in new code
36616      * @type {Roo.Layout}
36617      */
36618     panel : false,
36619     
36620     /**
36621      * @property layout
36622      * for BC  - use el in new code
36623      * @type {Roo.Layout}
36624      */
36625     layout : false,
36626     
36627      /**
36628      * @cfg {Function|boolean} disabled
36629      * If this module is disabled by some rule, return true from the funtion
36630      */
36631     disabled : false,
36632     
36633     /**
36634      * @cfg {String} parent 
36635      * Name of parent element which it get xtype added to..
36636      */
36637     parent: false,
36638     
36639     /**
36640      * @cfg {String} order
36641      * Used to set the order in which elements are created (usefull for multiple tabs)
36642      */
36643     
36644     order : false,
36645     /**
36646      * @cfg {String} name
36647      * String to display while loading.
36648      */
36649     name : false,
36650     /**
36651      * @cfg {String} region
36652      * Region to render component to (defaults to center)
36653      */
36654     region : 'center',
36655     
36656     /**
36657      * @cfg {Array} items
36658      * A single item array - the first element is the root of the tree..
36659      * It's done this way to stay compatible with the Xtype system...
36660      */
36661     items : false,
36662     
36663     
36664      /**
36665      * render
36666      * render element to dom or tree
36667      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
36668      */
36669     
36670     render : function(el)
36671     {
36672         
36673         if (!this.parent) {
36674             
36675             el = el ? Roo.get(el) : false;
36676             
36677             
36678             // it's a top level one..
36679             this.parent =  {
36680                 el : new Ext.BorderLayout(el || document.body, {
36681                 
36682                      center: {
36683                          titlebar: false,
36684                          autoScroll:false,
36685                          closeOnTab: true,
36686                          tabPosition: 'top',
36687                           //resizeTabs: true,
36688                          alwaysShowTabs: el ? false :  true,
36689                          minTabWidth: 140
36690                      }
36691                  })
36692             }
36693         }
36694             
36695         var tree = this.tree();
36696         tree.region = tree.region || this.region;
36697         this.el = this.parent.el.addxtype(tree);
36698         this.fireEvent('built', this);
36699         
36700         this.panel = this.el;
36701         this.layout = this.panel.layout;    
36702          
36703     }
36704     
36705      
36706      
36707     
36708 });
36709
36710 Roo.apply(Roo.XComponent, {
36711     
36712     /**
36713      * @property  buildCompleted
36714      * True when the builder has completed building the interface.
36715      * @type Boolean
36716      */
36717     buildCompleted : false,
36718      
36719     /**
36720      * @property  topModule
36721      * the upper most module - uses document.element as it's constructor.
36722      * @type Object
36723      */
36724      
36725     topModule  : false,
36726       
36727     /**
36728      * @property  modules
36729      * array of modules to be created by registration system.
36730      * @type Roo.XComponent
36731      */
36732     
36733     modules : [],
36734       
36735     
36736     /**
36737      * Register components to be built later.
36738      *
36739      * This solves the following issues
36740      * - Building is not done on page load, but after an authentication process has occured.
36741      * - Interface elements are registered on page load
36742      * - Parent Interface elements may not be loaded before child, so this handles that..
36743      * 
36744      *
36745      * example:
36746      * 
36747      * MyApp.register({
36748           order : '000001',
36749           module : 'Pman.Tab.projectMgr',
36750           region : 'center',
36751           parent : 'Pman.layout',
36752           disabled : false,  // or use a function..
36753         })
36754      
36755      * * @param {Object} details about module
36756      */
36757     register : function(obj) {
36758         this.modules.push(obj);
36759          
36760     },
36761     /**
36762      * convert a string to an object..
36763      * 
36764      */
36765     
36766     toObject : function(str)
36767     {
36768         if (!str || typeof(str) == 'object') {
36769             return str;
36770         }
36771         var ar = str.split('.');
36772         var rt, o;
36773         rt = ar.shift();
36774             /** eval:var:o */
36775         eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
36776         if (o === false) {
36777             throw "Module not found : " + str;
36778         }
36779         Roo.each(ar, function(e) {
36780             if (typeof(o[e]) == 'undefined') {
36781                 throw "Module not found : " + str;
36782             }
36783             o = o[e];
36784         });
36785         return o;
36786         
36787     },
36788     
36789     
36790     /**
36791      * move modules into their correct place in the tree..
36792      * 
36793      */
36794     preBuild : function ()
36795     {
36796         
36797         Roo.each(this.modules , function (obj)
36798         {
36799             obj.parent = this.toObject(obj.parent);
36800             
36801             if (!obj.parent) {
36802                 this.topModule = obj;
36803                 return;
36804             }
36805             
36806             if (!obj.parent.modules) {
36807                 obj.parent.modules = new Roo.util.MixedCollection(false, 
36808                     function(o) { return o.order + '' }
36809                 );
36810             }
36811             
36812             obj.parent.modules.add(obj);
36813         }, this);
36814     },
36815     
36816      /**
36817      * make a list of modules to build.
36818      * @return {Array} list of modules. 
36819      */ 
36820     
36821     buildOrder : function()
36822     {
36823         var _this = this;
36824         var cmp = function(a,b) {   
36825             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
36826         };
36827         
36828         if (!this.topModule || !this.topModule.modules) {
36829             throw "No top level modules to build";
36830         }
36831        
36832         // make a flat list in order of modules to build.
36833         var mods = [ this.topModule ];
36834         
36835         
36836         // add modules to their parents..
36837         var addMod = function(m) {
36838            // Roo.debug && Roo.log(m.modKey);
36839             
36840             mods.push(m);
36841             if (m.modules) {
36842                 m.modules.keySort('ASC',  cmp );
36843                 m.modules.each(addMod);
36844             }
36845             // not sure if this is used any more..
36846             if (m.finalize) {
36847                 m.finalize.name = m.name + " (clean up) ";
36848                 mods.push(m.finalize);
36849             }
36850             
36851         }
36852         this.topModule.modules.keySort('ASC',  cmp );
36853         this.topModule.modules.each(addMod);
36854         return mods;
36855     },
36856     
36857      /**
36858      * Build the registered modules.
36859      * @param {Object} parent element.
36860      * @param {Function} optional method to call after module has been added.
36861      * 
36862      */ 
36863    
36864     build : function() 
36865     {
36866         
36867         this.preBuild();
36868         var mods = this.buildOrder();
36869       
36870         //this.allmods = mods;
36871         //Roo.debug && Roo.log(mods);
36872         //return;
36873         if (!mods.length) { // should not happen
36874             throw "NO modules!!!";
36875         }
36876         
36877         
36878         
36879         // flash it up as modal - so we store the mask!?
36880         Roo.MessageBox.show({ title: 'loading' });
36881         Roo.MessageBox.show({
36882            title: "Please wait...",
36883            msg: "Building Interface...",
36884            width:450,
36885            progress:true,
36886            closable:false,
36887            modal: false
36888           
36889         });
36890         var total = mods.length;
36891         
36892         var _this = this;
36893         var progressRun = function() {
36894             if (!mods.length) {
36895                 Roo.debug && Roo.log('hide?');
36896                 Roo.MessageBox.hide();
36897                 _this.topModule.fireEvent('buildcomplete', _this.topModule);
36898                 return flase;    
36899             }
36900             
36901             var m = mods.shift();
36902             
36903             
36904             Roo.debug && Roo.log(m);
36905             // not sure if this is supported any more.. - modules that are are just function
36906             if (typeof(m) == 'function') { 
36907                 m.call(this);
36908                 return progressRun.defer(10, _this);
36909             } 
36910             
36911             
36912             
36913             Roo.MessageBox.updateProgress(
36914                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
36915                     " of " + total + 
36916                     (m.name ? (' - ' + m.name) : '')
36917                     );
36918             
36919          
36920             // is the module disabled?
36921             var disabled = (typeof(m.disabled) == 'function') ?
36922                 m.disabled.call(m.module.disabled) : m.disabled;    
36923             
36924             
36925             if (disabled) {
36926                 return progressRun(); // we do not update the display!
36927             }
36928             
36929             // now build 
36930             
36931             m.render();
36932             // it's 10 on top level, and 1 on others??? why...
36933             return progressRun.defer(10, _this);
36934              
36935         }
36936         progressRun.defer(1, _this);
36937      
36938         
36939         
36940     }
36941     
36942      
36943    
36944     
36945     
36946 });
36947  //<script type="text/javascript">
36948
36949
36950 /**
36951  * @class Roo.Login
36952  * @extends Roo.LayoutDialog
36953  * A generic Login Dialog..... - only one needed in theory!?!?
36954  *
36955  * Fires XComponent builder on success...
36956  * 
36957  * Sends 
36958  *    username,password, lang = for login actions.
36959  *    check = 1 for periodic checking that sesion is valid.
36960  *    passwordRequest = email request password
36961  *    logout = 1 = to logout
36962  * 
36963  * Affects: (this id="????" elements)
36964  *   loading  (removed) (used to indicate application is loading)
36965  *   loading-mask (hides) (used to hide application when it's building loading)
36966  *   
36967  * 
36968  * Usage: 
36969  *    
36970  * 
36971  * Myapp.login = Roo.Login({
36972      url: xxxx,
36973    
36974      realm : 'Myapp', 
36975      
36976      
36977      method : 'POST',
36978      
36979      
36980      * 
36981  })
36982  * 
36983  * 
36984  * 
36985  **/
36986  
36987 Roo.Login = function(cfg)
36988 {
36989     this.addEvents({
36990         'refreshed' : true
36991     });
36992     
36993     Roo.apply(this,cfg);
36994     
36995     Roo.onReady(function() {
36996         this.onLoad();
36997     }, this);
36998     // call parent..
36999     
37000    
37001     Roo.Login.superclass.constructor.call(this, this);
37002     //this.addxtype(this.items[0]);
37003     
37004     
37005 }
37006
37007
37008 Roo.extend(Roo.Login, Roo.LayoutDialog, {
37009     
37010     /**
37011      * @cfg {String} method
37012      * Method used to query for login details.
37013      */
37014     
37015     method : 'POST',
37016     /**
37017      * @cfg {String} url
37018      * URL to query login data. - eg. baseURL + '/Login.php'
37019      */
37020     url : '',
37021     
37022     /**
37023      * @property user
37024      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
37025      * @type {Object} 
37026      */
37027     user : false,
37028     /**
37029      * @property checkFails
37030      * Number of times we have attempted to get authentication check, and failed.
37031      * @type {Number} 
37032      */
37033     checkFails : 0,
37034       /**
37035      * @property intervalID
37036      * The window interval that does the constant login checking.
37037      * @type {Number} 
37038      */
37039     intervalID : 0,
37040     
37041     
37042     onLoad : function() // called on page load...
37043     {
37044         // load 
37045          
37046         if (Roo.get('loading')) { // clear any loading indicator..
37047             Roo.get('loading').remove();
37048         }
37049         
37050         //this.switchLang('en'); // set the language to english..
37051        
37052         this.check({
37053             success:  function(response, opts)  {  // check successfull...
37054             
37055                 var res = this.processResponse(response);
37056                 this.checkFails =0;
37057                 if (!res.success) { // error!
37058                     this.checkFails = 5;
37059                     //console.log('call failure');
37060                     return this.failure(response,opts);
37061                 }
37062                 
37063                 if (!res.data.id) { // id=0 == login failure.
37064                     return this.show();
37065                 }
37066                 
37067                               
37068                         //console.log(success);
37069                 this.fillAuth(res.data);   
37070                 this.checkFails =0;
37071                 Roo.XComponent.build();
37072             },
37073             failure : this.show
37074         });
37075         
37076     }, 
37077     
37078     
37079     check: function(cfg) // called every so often to refresh cookie etc..
37080     {
37081         if (cfg.again) { // could be undefined..
37082             this.checkFails++;
37083         } else {
37084             this.checkFails = 0;
37085         }
37086         var _this = this;
37087         if (this.sending) {
37088             if ( this.checkFails > 4) {
37089                 Roo.MessageBox.alert("Error",  
37090                     "Error getting authentication status. - try reloading, or wait a while", function() {
37091                         _this.sending = false;
37092                     }); 
37093                 return;
37094             }
37095             cfg.again = true;
37096             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
37097             return;
37098         }
37099         this.sending = true;
37100         
37101         Roo.Ajax.request({  
37102             url: this.url,
37103             params: {
37104                 getAuthUser: true
37105             },  
37106             method: this.method,
37107             success:  cfg.success || this.success,
37108             failure : cfg.failure || this.failure,
37109             scope : this,
37110             callCfg : cfg
37111               
37112         });  
37113     }, 
37114     
37115     
37116     logout: function()
37117     {
37118         window.onbeforeunload = function() { }; // false does not work for IE..
37119         this.user = false;
37120         var _this = this;
37121         
37122         Roo.Ajax.request({  
37123             url: this.url,
37124             params: {
37125                 logout: 1
37126             },  
37127             method: 'GET',
37128             failure : function() {
37129                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
37130                     document.location = document.location.toString() + '?ts=' + Math.random();
37131                 });
37132                 
37133             },
37134             success : function() {
37135                 _this.user = false;
37136                 this.checkFails =0;
37137                 // fixme..
37138                 document.location = document.location.toString() + '?ts=' + Math.random();
37139             }
37140               
37141               
37142         }); 
37143     },
37144     
37145     processResponse : function (response)
37146     {
37147         var res = '';
37148         try {
37149             res = Roo.decode(response.responseText);
37150             // oops...
37151             if (typeof(res) != 'object') {
37152                 res = { success : false, errorMsg : res, errors : true };
37153             }
37154             if (typeof(res.success) == 'undefined') {
37155                 res.success = false;
37156             }
37157             
37158         } catch(e) {
37159             res = { success : false,  errorMsg : response.responseText, errors : true };
37160         }
37161         return res;
37162     },
37163     
37164     success : function(response, opts)  // check successfull...
37165     {  
37166         this.sending = false;
37167         var res = this.processResponse(response);
37168         if (!res.success) {
37169             return this.failure(response, opts);
37170         }
37171         if (!res.data || !res.data.id) {
37172             return this.failure(response,opts);
37173         }
37174         //console.log(res);
37175         this.fillAuth(res.data);
37176         
37177         this.checkFails =0;
37178         
37179     },
37180     
37181     
37182     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
37183     {
37184         this.authUser = -1;
37185         this.sending = false;
37186         var res = this.processResponse(response);
37187         //console.log(res);
37188         if ( this.checkFails > 2) {
37189         
37190             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
37191                 "Error getting authentication status. - try reloading"); 
37192             return;
37193         }
37194         opts.callCfg.again = true;
37195         this.check.defer(1000, this, [ opts.callCfg ]);
37196         return;  
37197     },
37198     
37199     
37200     
37201     fillAuth: function(au) {
37202         this.startAuthCheck();
37203         this.authUserId = au.id;
37204         this.authUser = au;
37205         this.lastChecked = new Date();
37206         this.fireEvent('refreshed', au);
37207         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
37208         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
37209         au.lang = au.lang || 'en';
37210         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
37211         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
37212         this.switchLang(au.lang );
37213         
37214      
37215         // open system... - -on setyp..
37216         if (this.authUserId  < 0) {
37217             Roo.MessageBox.alert("Warning", 
37218                 "This is an open system - please set up a admin user with a password.");  
37219         }
37220          
37221         //Pman.onload(); // which should do nothing if it's a re-auth result...
37222         
37223              
37224     },
37225     
37226     startAuthCheck : function() // starter for timeout checking..
37227     {
37228         if (this.intervalID) { // timer already in place...
37229             return false;
37230         }
37231         var _this = this;
37232         this.intervalID =  window.setInterval(function() {
37233               _this.check(false);
37234             }, 120000); // every 120 secs = 2mins..
37235         
37236         
37237     },
37238          
37239     
37240     switchLang : function (lang) 
37241     {
37242         _T = typeof(_T) == 'undefined' ? false : _T;
37243           if (!_T || !lang.length) {
37244             return;
37245         }
37246         
37247         if (!_T && lang != 'en') {
37248             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
37249             return;
37250         }
37251         
37252         if (typeof(_T.en) == 'undefined') {
37253             _T.en = {};
37254             Roo.apply(_T.en, _T);
37255         }
37256         
37257         if (typeof(_T[lang]) == 'undefined') {
37258             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
37259             return;
37260         }
37261         
37262         
37263         Roo.apply(_T, _T[lang]);
37264         // just need to set the text values for everything...
37265         var _this = this;
37266         /* this will not work ...
37267         if (this.form) { 
37268             
37269                
37270             function formLabel(name, val) {
37271                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
37272             }
37273             
37274             formLabel('password', "Password"+':');
37275             formLabel('username', "Email Address"+':');
37276             formLabel('lang', "Language"+':');
37277             this.dialog.setTitle("Login");
37278             this.dialog.buttons[0].setText("Forgot Password");
37279             this.dialog.buttons[1].setText("Login");
37280         }
37281         */
37282         
37283         
37284     },
37285     
37286     
37287     title: "Login",
37288     modal: true,
37289     width:  350,
37290     //height: 230,
37291     height: 180,
37292     shadow: true,
37293     minWidth:200,
37294     minHeight:180,
37295     //proxyDrag: true,
37296     closable: false,
37297     draggable: false,
37298     collapsible: false,
37299     resizable: false,
37300     center: {  // needed??
37301         autoScroll:false,
37302         titlebar: false,
37303        // tabPosition: 'top',
37304         hideTabs: true,
37305         closeOnTab: true,
37306         alwaysShowTabs: false
37307     } ,
37308     listeners : {
37309         
37310         show  : function(dlg)
37311         {
37312             //console.log(this);
37313             this.form = this.layout.getRegion('center').activePanel.form;
37314             this.form.dialog = dlg;
37315             this.buttons[0].form = this.form;
37316             this.buttons[0].dialog = dlg;
37317             this.buttons[1].form = this.form;
37318             this.buttons[1].dialog = dlg;
37319            
37320            //this.resizeToLogo.defer(1000,this);
37321             // this is all related to resizing for logos..
37322             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
37323            //// if (!sz) {
37324              //   this.resizeToLogo.defer(1000,this);
37325              //   return;
37326            // }
37327             //var w = Ext.lib.Dom.getViewWidth() - 100;
37328             //var h = Ext.lib.Dom.getViewHeight() - 100;
37329             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
37330             //this.center();
37331             if (this.disabled) {
37332                 this.hide();
37333                 return;
37334             }
37335             
37336             if (this.user.id < 0) { // used for inital setup situations.
37337                 return;
37338             }
37339             
37340             if (this.intervalID) {
37341                 // remove the timer
37342                 window.clearInterval(this.intervalID);
37343                 this.intervalID = false;
37344             }
37345             
37346             
37347             if (Roo.get('loading')) {
37348                 Roo.get('loading').remove();
37349             }
37350             if (Roo.get('loading-mask')) {
37351                 Roo.get('loading-mask').hide();
37352             }
37353             
37354             //incomming._node = tnode;
37355             this.form.reset();
37356             //this.dialog.modal = !modal;
37357             //this.dialog.show();
37358             this.el.unmask(); 
37359             
37360             
37361             this.form.setValues({
37362                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
37363                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
37364             });
37365             
37366             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
37367             if (this.form.findField('username').getValue().length > 0 ){
37368                 this.form.findField('password').focus();
37369             } else {
37370                this.form.findField('username').focus();
37371             }
37372     
37373         }
37374     },
37375     items : [
37376          {
37377        
37378             xtype : 'ContentPanel',
37379             xns : Roo,
37380             region: 'center',
37381             fitToFrame : true,
37382             
37383             items : [
37384     
37385                 {
37386                
37387                     xtype : 'Form',
37388                     xns : Roo.form,
37389                     labelWidth: 100,
37390                     style : 'margin: 10px;',
37391                     
37392                     listeners : {
37393                         actionfailed : function(f, act) {
37394                             // form can return { errors: .... }
37395                                 
37396                             //act.result.errors // invalid form element list...
37397                             //act.result.errorMsg// invalid form element list...
37398                             
37399                             this.dialog.el.unmask();
37400                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
37401                                         "Login failed - communication error - try again.");
37402                                       
37403                         },
37404                         actioncomplete: function(re, act) {
37405                              
37406                             Roo.state.Manager.set(
37407                                 this.dialog.realm + '.username',  
37408                                     this.findField('username').getValue()
37409                             );
37410                             Roo.state.Manager.set(
37411                                 this.dialog.realm + '.lang',  
37412                                 this.findField('lang').getValue() 
37413                             );
37414                             
37415                             this.dialog.fillAuth(act.result.data);
37416                               
37417                             this.dialog.hide();
37418                             
37419                             if (Roo.get('loading-mask')) {
37420                                 Roo.get('loading-mask').show();
37421                             }
37422                             Roo.XComponent.build();
37423                             
37424                              
37425                             
37426                         }
37427                     },
37428                     items : [
37429                         {
37430                             xtype : 'TextField',
37431                             xns : Roo.form,
37432                             fieldLabel: "Email Address",
37433                             name: 'username',
37434                             width:200,
37435                             autoCreate : {tag: "input", type: "text", size: "20"}
37436                         },
37437                         {
37438                             xtype : 'TextField',
37439                             xns : Roo.form,
37440                             fieldLabel: "Password",
37441                             inputType: 'password',
37442                             name: 'password',
37443                             width:200,
37444                             autoCreate : {tag: "input", type: "text", size: "20"},
37445                             listeners : {
37446                                 specialkey : function(e,ev) {
37447                                     if (ev.keyCode == 13) {
37448                                         this.form.dialog.el.mask("Logging in");
37449                                         this.form.doAction('submit', {
37450                                             url: this.form.dialog.url,
37451                                             method: this.form.dialog.method
37452                                         });
37453                                     }
37454                                 }
37455                             }  
37456                         },
37457                         {
37458                             xtype : 'ComboBox',
37459                             xns : Roo.form,
37460                             fieldLabel: "Language",
37461                             name : 'langdisp',
37462                             store: {
37463                                 xtype : 'SimpleStore',
37464                                 fields: ['lang', 'ldisp'],
37465                                 data : [
37466                                     [ 'en', 'English' ],
37467                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
37468                                     [ 'zh_CN', '\u7C21\u4E2D' ]
37469                                 ]
37470                             },
37471                             
37472                             valueField : 'lang',
37473                             hiddenName:  'lang',
37474                             width: 200,
37475                             displayField:'ldisp',
37476                             typeAhead: false,
37477                             editable: false,
37478                             mode: 'local',
37479                             triggerAction: 'all',
37480                             emptyText:'Select a Language...',
37481                             selectOnFocus:true,
37482                             listeners : {
37483                                 select :  function(cb, rec, ix) {
37484                                     this.form.switchLang(rec.data.lang);
37485                                 }
37486                             }
37487                         
37488                         }
37489                     ]
37490                 }
37491                   
37492                 
37493             ]
37494         }
37495     ],
37496     buttons : [
37497         {
37498             xtype : 'Button',
37499             xns : 'Roo',
37500             text : "Forgot Password",
37501             listeners : {
37502                 click : function() {
37503                     //console.log(this);
37504                     var n = this.form.findField('username').getValue();
37505                     if (!n.length) {
37506                         Roo.MessageBox.alert("Error", "Fill in your email address");
37507                         return;
37508                     }
37509                     Roo.Ajax.request({
37510                         url: this.dialog.url,
37511                         params: {
37512                             passwordRequest: n
37513                         },
37514                         method: this.dialog.method,
37515                         success:  function(response, opts)  {  // check successfull...
37516                         
37517                             var res = this.dialog.processResponse(response);
37518                             if (!res.success) { // error!
37519                                Roo.MessageBox.alert("Error" ,
37520                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
37521                                return;
37522                             }
37523                             Roo.MessageBox.alert("Notice" ,
37524                                 "Please check you email for the Password Reset message");
37525                         },
37526                         failure : function() {
37527                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
37528                         }
37529                         
37530                     });
37531                 }
37532             }
37533         },
37534         {
37535             xtype : 'Button',
37536             xns : 'Roo',
37537             text : "Login",
37538             listeners : {
37539                 
37540                 click : function () {
37541                         
37542                     this.dialog.el.mask("Logging in");
37543                     this.form.doAction('submit', {
37544                             url: this.dialog.url,
37545                             method: this.dialog.method
37546                     });
37547                 }
37548             }
37549         }
37550     ]
37551   
37552   
37553 })
37554  
37555
37556
37557