Update documentation
[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         "multisort" : "_multisort"
4725     };
4726
4727     if(config && config.data){
4728         this.inlineData = config.data;
4729         delete config.data;
4730     }
4731
4732     Roo.apply(this, config);
4733     
4734     if(this.reader){ // reader passed
4735         this.reader = Roo.factory(this.reader, Roo.data);
4736         this.reader.xmodule = this.xmodule || false;
4737         if(!this.recordType){
4738             this.recordType = this.reader.recordType;
4739         }
4740         if(this.reader.onMetaChange){
4741             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4742         }
4743     }
4744
4745     if(this.recordType){
4746         this.fields = this.recordType.prototype.fields;
4747     }
4748     this.modified = [];
4749
4750     this.addEvents({
4751         /**
4752          * @event datachanged
4753          * Fires when the data cache has changed, and a widget which is using this Store
4754          * as a Record cache should refresh its view.
4755          * @param {Store} this
4756          */
4757         datachanged : true,
4758         /**
4759          * @event metachange
4760          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4761          * @param {Store} this
4762          * @param {Object} meta The JSON metadata
4763          */
4764         metachange : true,
4765         /**
4766          * @event add
4767          * Fires when Records have been added to the Store
4768          * @param {Store} this
4769          * @param {Roo.data.Record[]} records The array of Records added
4770          * @param {Number} index The index at which the record(s) were added
4771          */
4772         add : true,
4773         /**
4774          * @event remove
4775          * Fires when a Record has been removed from the Store
4776          * @param {Store} this
4777          * @param {Roo.data.Record} record The Record that was removed
4778          * @param {Number} index The index at which the record was removed
4779          */
4780         remove : true,
4781         /**
4782          * @event update
4783          * Fires when a Record has been updated
4784          * @param {Store} this
4785          * @param {Roo.data.Record} record The Record that was updated
4786          * @param {String} operation The update operation being performed.  Value may be one of:
4787          * <pre><code>
4788  Roo.data.Record.EDIT
4789  Roo.data.Record.REJECT
4790  Roo.data.Record.COMMIT
4791          * </code></pre>
4792          */
4793         update : true,
4794         /**
4795          * @event clear
4796          * Fires when the data cache has been cleared.
4797          * @param {Store} this
4798          */
4799         clear : true,
4800         /**
4801          * @event beforeload
4802          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4803          * the load action will be canceled.
4804          * @param {Store} this
4805          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4806          */
4807         beforeload : true,
4808         /**
4809          * @event load
4810          * Fires after a new set of Records has been loaded.
4811          * @param {Store} this
4812          * @param {Roo.data.Record[]} records The Records that were loaded
4813          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4814          */
4815         load : true,
4816         /**
4817          * @event loadexception
4818          * Fires if an exception occurs in the Proxy during loading.
4819          * Called with the signature of the Proxy's "loadexception" event.
4820          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4821          * 
4822          * @param {Proxy} 
4823          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4824          * @param {Object} load options 
4825          * @param {Object} jsonData from your request (normally this contains the Exception)
4826          */
4827         loadexception : true
4828     });
4829     
4830     if(this.proxy){
4831         this.proxy = Roo.factory(this.proxy, Roo.data);
4832         this.proxy.xmodule = this.xmodule || false;
4833         this.relayEvents(this.proxy,  ["loadexception"]);
4834     }
4835     this.sortToggle = {};
4836     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
4837
4838     Roo.data.Store.superclass.constructor.call(this);
4839
4840     if(this.inlineData){
4841         this.loadData(this.inlineData);
4842         delete this.inlineData;
4843     }
4844 };
4845 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4846      /**
4847     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4848     * without a remote query - used by combo/forms at present.
4849     */
4850     
4851     /**
4852     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4853     */
4854     /**
4855     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4856     */
4857     /**
4858     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4859     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4860     */
4861     /**
4862     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4863     * on any HTTP request
4864     */
4865     /**
4866     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4867     */
4868     /**
4869     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
4870     */
4871     multiSort: false,
4872     /**
4873     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4874     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4875     */
4876     remoteSort : false,
4877
4878     /**
4879     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4880      * loaded or when a record is removed. (defaults to false).
4881     */
4882     pruneModifiedRecords : false,
4883
4884     // private
4885     lastOptions : null,
4886
4887     /**
4888      * Add Records to the Store and fires the add event.
4889      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4890      */
4891     add : function(records){
4892         records = [].concat(records);
4893         for(var i = 0, len = records.length; i < len; i++){
4894             records[i].join(this);
4895         }
4896         var index = this.data.length;
4897         this.data.addAll(records);
4898         this.fireEvent("add", this, records, index);
4899     },
4900
4901     /**
4902      * Remove a Record from the Store and fires the remove event.
4903      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4904      */
4905     remove : function(record){
4906         var index = this.data.indexOf(record);
4907         this.data.removeAt(index);
4908         if(this.pruneModifiedRecords){
4909             this.modified.remove(record);
4910         }
4911         this.fireEvent("remove", this, record, index);
4912     },
4913
4914     /**
4915      * Remove all Records from the Store and fires the clear event.
4916      */
4917     removeAll : function(){
4918         this.data.clear();
4919         if(this.pruneModifiedRecords){
4920             this.modified = [];
4921         }
4922         this.fireEvent("clear", this);
4923     },
4924
4925     /**
4926      * Inserts Records to the Store at the given index and fires the add event.
4927      * @param {Number} index The start index at which to insert the passed Records.
4928      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4929      */
4930     insert : function(index, records){
4931         records = [].concat(records);
4932         for(var i = 0, len = records.length; i < len; i++){
4933             this.data.insert(index, records[i]);
4934             records[i].join(this);
4935         }
4936         this.fireEvent("add", this, records, index);
4937     },
4938
4939     /**
4940      * Get the index within the cache of the passed Record.
4941      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4942      * @return {Number} The index of the passed Record. Returns -1 if not found.
4943      */
4944     indexOf : function(record){
4945         return this.data.indexOf(record);
4946     },
4947
4948     /**
4949      * Get the index within the cache of the Record with the passed id.
4950      * @param {String} id The id of the Record to find.
4951      * @return {Number} The index of the Record. Returns -1 if not found.
4952      */
4953     indexOfId : function(id){
4954         return this.data.indexOfKey(id);
4955     },
4956
4957     /**
4958      * Get the Record with the specified id.
4959      * @param {String} id The id of the Record to find.
4960      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4961      */
4962     getById : function(id){
4963         return this.data.key(id);
4964     },
4965
4966     /**
4967      * Get the Record at the specified index.
4968      * @param {Number} index The index of the Record to find.
4969      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
4970      */
4971     getAt : function(index){
4972         return this.data.itemAt(index);
4973     },
4974
4975     /**
4976      * Returns a range of Records between specified indices.
4977      * @param {Number} startIndex (optional) The starting index (defaults to 0)
4978      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
4979      * @return {Roo.data.Record[]} An array of Records
4980      */
4981     getRange : function(start, end){
4982         return this.data.getRange(start, end);
4983     },
4984
4985     // private
4986     storeOptions : function(o){
4987         o = Roo.apply({}, o);
4988         delete o.callback;
4989         delete o.scope;
4990         this.lastOptions = o;
4991     },
4992
4993     /**
4994      * Loads the Record cache from the configured Proxy using the configured Reader.
4995      * <p>
4996      * If using remote paging, then the first load call must specify the <em>start</em>
4997      * and <em>limit</em> properties in the options.params property to establish the initial
4998      * position within the dataset, and the number of Records to cache on each read from the Proxy.
4999      * <p>
5000      * <strong>It is important to note that for remote data sources, loading is asynchronous,
5001      * and this call will return before the new data has been loaded. Perform any post-processing
5002      * in a callback function, or in a "load" event handler.</strong>
5003      * <p>
5004      * @param {Object} options An object containing properties which control loading options:<ul>
5005      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5006      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5007      * passed the following arguments:<ul>
5008      * <li>r : Roo.data.Record[]</li>
5009      * <li>options: Options object from the load call</li>
5010      * <li>success: Boolean success indicator</li></ul></li>
5011      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5012      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5013      * </ul>
5014      */
5015     load : function(options){
5016         options = options || {};
5017         if(this.fireEvent("beforeload", this, options) !== false){
5018             this.storeOptions(options);
5019             var p = Roo.apply(options.params || {}, this.baseParams);
5020             // if meta was not loaded from remote source.. try requesting it.
5021             if (!this.reader.metaFromRemote) {
5022                 p._requestMeta = 1;
5023             }
5024             if(this.sortInfo && this.remoteSort){
5025                 var pn = this.paramNames;
5026                 p[pn["sort"]] = this.sortInfo.field;
5027                 p[pn["dir"]] = this.sortInfo.direction;
5028             }
5029             if (this.multiSort) {
5030                 var pn = this.paramNames;
5031                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
5032             }
5033             
5034             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5035         }
5036     },
5037
5038     /**
5039      * Reloads the Record cache from the configured Proxy using the configured Reader and
5040      * the options from the last load operation performed.
5041      * @param {Object} options (optional) An object containing properties which may override the options
5042      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5043      * the most recently used options are reused).
5044      */
5045     reload : function(options){
5046         this.load(Roo.applyIf(options||{}, this.lastOptions));
5047     },
5048
5049     // private
5050     // Called as a callback by the Reader during a load operation.
5051     loadRecords : function(o, options, success){
5052         if(!o || success === false){
5053             if(success !== false){
5054                 this.fireEvent("load", this, [], options);
5055             }
5056             if(options.callback){
5057                 options.callback.call(options.scope || this, [], options, false);
5058             }
5059             return;
5060         }
5061         // if data returned failure - throw an exception.
5062         if (o.success === false) {
5063             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
5064             return;
5065         }
5066         var r = o.records, t = o.totalRecords || r.length;
5067         if(!options || options.add !== true){
5068             if(this.pruneModifiedRecords){
5069                 this.modified = [];
5070             }
5071             for(var i = 0, len = r.length; i < len; i++){
5072                 r[i].join(this);
5073             }
5074             if(this.snapshot){
5075                 this.data = this.snapshot;
5076                 delete this.snapshot;
5077             }
5078             this.data.clear();
5079             this.data.addAll(r);
5080             this.totalLength = t;
5081             this.applySort();
5082             this.fireEvent("datachanged", this);
5083         }else{
5084             this.totalLength = Math.max(t, this.data.length+r.length);
5085             this.add(r);
5086         }
5087         this.fireEvent("load", this, r, options);
5088         if(options.callback){
5089             options.callback.call(options.scope || this, r, options, true);
5090         }
5091     },
5092
5093     /**
5094      * Loads data from a passed data block. A Reader which understands the format of the data
5095      * must have been configured in the constructor.
5096      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5097      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5098      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5099      */
5100     loadData : function(o, append){
5101         var r = this.reader.readRecords(o);
5102         this.loadRecords(r, {add: append}, true);
5103     },
5104
5105     /**
5106      * Gets the number of cached records.
5107      * <p>
5108      * <em>If using paging, this may not be the total size of the dataset. If the data object
5109      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5110      * the data set size</em>
5111      */
5112     getCount : function(){
5113         return this.data.length || 0;
5114     },
5115
5116     /**
5117      * Gets the total number of records in the dataset as returned by the server.
5118      * <p>
5119      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5120      * the dataset size</em>
5121      */
5122     getTotalCount : function(){
5123         return this.totalLength || 0;
5124     },
5125
5126     /**
5127      * Returns the sort state of the Store as an object with two properties:
5128      * <pre><code>
5129  field {String} The name of the field by which the Records are sorted
5130  direction {String} The sort order, "ASC" or "DESC"
5131      * </code></pre>
5132      */
5133     getSortState : function(){
5134         return this.sortInfo;
5135     },
5136
5137     // private
5138     applySort : function(){
5139         if(this.sortInfo && !this.remoteSort){
5140             var s = this.sortInfo, f = s.field;
5141             var st = this.fields.get(f).sortType;
5142             var fn = function(r1, r2){
5143                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5144                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5145             };
5146             this.data.sort(s.direction, fn);
5147             if(this.snapshot && this.snapshot != this.data){
5148                 this.snapshot.sort(s.direction, fn);
5149             }
5150         }
5151     },
5152
5153     /**
5154      * Sets the default sort column and order to be used by the next load operation.
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     setDefaultSort : function(field, dir){
5159         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5160     },
5161
5162     /**
5163      * Sort the Records.
5164      * If remote sorting is used, the sort is performed on the server, and the cache is
5165      * reloaded. If local sorting is used, the cache is sorted internally.
5166      * @param {String} fieldName The name of the field to sort by.
5167      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5168      */
5169     sort : function(fieldName, dir){
5170         var f = this.fields.get(fieldName);
5171         if(!dir){
5172             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
5173             
5174             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
5175                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5176             }else{
5177                 dir = f.sortDir;
5178             }
5179         }
5180         this.sortToggle[f.name] = dir;
5181         this.sortInfo = {field: f.name, direction: dir};
5182         if(!this.remoteSort){
5183             this.applySort();
5184             this.fireEvent("datachanged", this);
5185         }else{
5186             this.load(this.lastOptions);
5187         }
5188     },
5189
5190     /**
5191      * Calls the specified function for each of the Records in the cache.
5192      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5193      * Returning <em>false</em> aborts and exits the iteration.
5194      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5195      */
5196     each : function(fn, scope){
5197         this.data.each(fn, scope);
5198     },
5199
5200     /**
5201      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5202      * (e.g., during paging).
5203      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5204      */
5205     getModifiedRecords : function(){
5206         return this.modified;
5207     },
5208
5209     // private
5210     createFilterFn : function(property, value, anyMatch){
5211         if(!value.exec){ // not a regex
5212             value = String(value);
5213             if(value.length == 0){
5214                 return false;
5215             }
5216             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5217         }
5218         return function(r){
5219             return value.test(r.data[property]);
5220         };
5221     },
5222
5223     /**
5224      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5225      * @param {String} property A field on your records
5226      * @param {Number} start The record index to start at (defaults to 0)
5227      * @param {Number} end The last record index to include (defaults to length - 1)
5228      * @return {Number} The sum
5229      */
5230     sum : function(property, start, end){
5231         var rs = this.data.items, v = 0;
5232         start = start || 0;
5233         end = (end || end === 0) ? end : rs.length-1;
5234
5235         for(var i = start; i <= end; i++){
5236             v += (rs[i].data[property] || 0);
5237         }
5238         return v;
5239     },
5240
5241     /**
5242      * Filter the records by a specified property.
5243      * @param {String} field A field on your records
5244      * @param {String/RegExp} value Either a string that the field
5245      * should start with or a RegExp to test against the field
5246      * @param {Boolean} anyMatch True to match any part not just the beginning
5247      */
5248     filter : function(property, value, anyMatch){
5249         var fn = this.createFilterFn(property, value, anyMatch);
5250         return fn ? this.filterBy(fn) : this.clearFilter();
5251     },
5252
5253     /**
5254      * Filter by a function. The specified function will be called with each
5255      * record in this data source. If the function returns true the record is included,
5256      * otherwise it is filtered.
5257      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5258      * @param {Object} scope (optional) The scope of the function (defaults to this)
5259      */
5260     filterBy : function(fn, scope){
5261         this.snapshot = this.snapshot || this.data;
5262         this.data = this.queryBy(fn, scope||this);
5263         this.fireEvent("datachanged", this);
5264     },
5265
5266     /**
5267      * Query the records by a specified property.
5268      * @param {String} field A field on your records
5269      * @param {String/RegExp} value Either a string that the field
5270      * should start with or a RegExp to test against the field
5271      * @param {Boolean} anyMatch True to match any part not just the beginning
5272      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5273      */
5274     query : function(property, value, anyMatch){
5275         var fn = this.createFilterFn(property, value, anyMatch);
5276         return fn ? this.queryBy(fn) : this.data.clone();
5277     },
5278
5279     /**
5280      * Query by a function. The specified function will be called with each
5281      * record in this data source. If the function returns true the record is included
5282      * in the results.
5283      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5284      * @param {Object} scope (optional) The scope of the function (defaults to this)
5285       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5286      **/
5287     queryBy : function(fn, scope){
5288         var data = this.snapshot || this.data;
5289         return data.filterBy(fn, scope||this);
5290     },
5291
5292     /**
5293      * Collects unique values for a particular dataIndex from this store.
5294      * @param {String} dataIndex The property to collect
5295      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5296      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5297      * @return {Array} An array of the unique values
5298      **/
5299     collect : function(dataIndex, allowNull, bypassFilter){
5300         var d = (bypassFilter === true && this.snapshot) ?
5301                 this.snapshot.items : this.data.items;
5302         var v, sv, r = [], l = {};
5303         for(var i = 0, len = d.length; i < len; i++){
5304             v = d[i].data[dataIndex];
5305             sv = String(v);
5306             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5307                 l[sv] = true;
5308                 r[r.length] = v;
5309             }
5310         }
5311         return r;
5312     },
5313
5314     /**
5315      * Revert to a view of the Record cache with no filtering applied.
5316      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5317      */
5318     clearFilter : function(suppressEvent){
5319         if(this.snapshot && this.snapshot != this.data){
5320             this.data = this.snapshot;
5321             delete this.snapshot;
5322             if(suppressEvent !== true){
5323                 this.fireEvent("datachanged", this);
5324             }
5325         }
5326     },
5327
5328     // private
5329     afterEdit : function(record){
5330         if(this.modified.indexOf(record) == -1){
5331             this.modified.push(record);
5332         }
5333         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5334     },
5335
5336     // private
5337     afterReject : function(record){
5338         this.modified.remove(record);
5339         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5340     },
5341
5342     // private
5343     afterCommit : function(record){
5344         this.modified.remove(record);
5345         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5346     },
5347
5348     /**
5349      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5350      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5351      */
5352     commitChanges : function(){
5353         var m = this.modified.slice(0);
5354         this.modified = [];
5355         for(var i = 0, len = m.length; i < len; i++){
5356             m[i].commit();
5357         }
5358     },
5359
5360     /**
5361      * Cancel outstanding changes on all changed records.
5362      */
5363     rejectChanges : function(){
5364         var m = this.modified.slice(0);
5365         this.modified = [];
5366         for(var i = 0, len = m.length; i < len; i++){
5367             m[i].reject();
5368         }
5369     },
5370
5371     onMetaChange : function(meta, rtype, o){
5372         this.recordType = rtype;
5373         this.fields = rtype.prototype.fields;
5374         delete this.snapshot;
5375         this.sortInfo = meta.sortInfo || this.sortInfo;
5376         this.modified = [];
5377         this.fireEvent('metachange', this, this.reader.meta);
5378     }
5379 });/*
5380  * Based on:
5381  * Ext JS Library 1.1.1
5382  * Copyright(c) 2006-2007, Ext JS, LLC.
5383  *
5384  * Originally Released Under LGPL - original licence link has changed is not relivant.
5385  *
5386  * Fork - LGPL
5387  * <script type="text/javascript">
5388  */
5389
5390 /**
5391  * @class Roo.data.SimpleStore
5392  * @extends Roo.data.Store
5393  * Small helper class to make creating Stores from Array data easier.
5394  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5395  * @cfg {Array} fields An array of field definition objects, or field name strings.
5396  * @cfg {Array} data The multi-dimensional array of data
5397  * @constructor
5398  * @param {Object} config
5399  */
5400 Roo.data.SimpleStore = function(config){
5401     Roo.data.SimpleStore.superclass.constructor.call(this, {
5402         isLocal : true,
5403         reader: new Roo.data.ArrayReader({
5404                 id: config.id
5405             },
5406             Roo.data.Record.create(config.fields)
5407         ),
5408         proxy : new Roo.data.MemoryProxy(config.data)
5409     });
5410     this.load();
5411 };
5412 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5413  * Based on:
5414  * Ext JS Library 1.1.1
5415  * Copyright(c) 2006-2007, Ext JS, LLC.
5416  *
5417  * Originally Released Under LGPL - original licence link has changed is not relivant.
5418  *
5419  * Fork - LGPL
5420  * <script type="text/javascript">
5421  */
5422
5423 /**
5424 /**
5425  * @extends Roo.data.Store
5426  * @class Roo.data.JsonStore
5427  * Small helper class to make creating Stores for JSON data easier. <br/>
5428 <pre><code>
5429 var store = new Roo.data.JsonStore({
5430     url: 'get-images.php',
5431     root: 'images',
5432     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5433 });
5434 </code></pre>
5435  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5436  * JsonReader and HttpProxy (unless inline data is provided).</b>
5437  * @cfg {Array} fields An array of field definition objects, or field name strings.
5438  * @constructor
5439  * @param {Object} config
5440  */
5441 Roo.data.JsonStore = function(c){
5442     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5443         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5444         reader: new Roo.data.JsonReader(c, c.fields)
5445     }));
5446 };
5447 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5448  * Based on:
5449  * Ext JS Library 1.1.1
5450  * Copyright(c) 2006-2007, Ext JS, LLC.
5451  *
5452  * Originally Released Under LGPL - original licence link has changed is not relivant.
5453  *
5454  * Fork - LGPL
5455  * <script type="text/javascript">
5456  */
5457
5458  
5459 Roo.data.Field = function(config){
5460     if(typeof config == "string"){
5461         config = {name: config};
5462     }
5463     Roo.apply(this, config);
5464     
5465     if(!this.type){
5466         this.type = "auto";
5467     }
5468     
5469     var st = Roo.data.SortTypes;
5470     // named sortTypes are supported, here we look them up
5471     if(typeof this.sortType == "string"){
5472         this.sortType = st[this.sortType];
5473     }
5474     
5475     // set default sortType for strings and dates
5476     if(!this.sortType){
5477         switch(this.type){
5478             case "string":
5479                 this.sortType = st.asUCString;
5480                 break;
5481             case "date":
5482                 this.sortType = st.asDate;
5483                 break;
5484             default:
5485                 this.sortType = st.none;
5486         }
5487     }
5488
5489     // define once
5490     var stripRe = /[\$,%]/g;
5491
5492     // prebuilt conversion function for this field, instead of
5493     // switching every time we're reading a value
5494     if(!this.convert){
5495         var cv, dateFormat = this.dateFormat;
5496         switch(this.type){
5497             case "":
5498             case "auto":
5499             case undefined:
5500                 cv = function(v){ return v; };
5501                 break;
5502             case "string":
5503                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5504                 break;
5505             case "int":
5506                 cv = function(v){
5507                     return v !== undefined && v !== null && v !== '' ?
5508                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5509                     };
5510                 break;
5511             case "float":
5512                 cv = function(v){
5513                     return v !== undefined && v !== null && v !== '' ?
5514                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5515                     };
5516                 break;
5517             case "bool":
5518             case "boolean":
5519                 cv = function(v){ return v === true || v === "true" || v == 1; };
5520                 break;
5521             case "date":
5522                 cv = function(v){
5523                     if(!v){
5524                         return '';
5525                     }
5526                     if(v instanceof Date){
5527                         return v;
5528                     }
5529                     if(dateFormat){
5530                         if(dateFormat == "timestamp"){
5531                             return new Date(v*1000);
5532                         }
5533                         return Date.parseDate(v, dateFormat);
5534                     }
5535                     var parsed = Date.parse(v);
5536                     return parsed ? new Date(parsed) : null;
5537                 };
5538              break;
5539             
5540         }
5541         this.convert = cv;
5542     }
5543 };
5544
5545 Roo.data.Field.prototype = {
5546     dateFormat: null,
5547     defaultValue: "",
5548     mapping: null,
5549     sortType : null,
5550     sortDir : "ASC"
5551 };/*
5552  * Based on:
5553  * Ext JS Library 1.1.1
5554  * Copyright(c) 2006-2007, Ext JS, LLC.
5555  *
5556  * Originally Released Under LGPL - original licence link has changed is not relivant.
5557  *
5558  * Fork - LGPL
5559  * <script type="text/javascript">
5560  */
5561  
5562 // Base class for reading structured data from a data source.  This class is intended to be
5563 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5564
5565 /**
5566  * @class Roo.data.DataReader
5567  * Base class for reading structured data from a data source.  This class is intended to be
5568  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5569  */
5570
5571 Roo.data.DataReader = function(meta, recordType){
5572     
5573     this.meta = meta;
5574     
5575     this.recordType = recordType instanceof Array ? 
5576         Roo.data.Record.create(recordType) : recordType;
5577 };
5578
5579 Roo.data.DataReader.prototype = {
5580      /**
5581      * Create an empty record
5582      * @param {Object} data (optional) - overlay some values
5583      * @return {Roo.data.Record} record created.
5584      */
5585     newRow :  function(d) {
5586         var da =  {};
5587         this.recordType.prototype.fields.each(function(c) {
5588             switch( c.type) {
5589                 case 'int' : da[c.name] = 0; break;
5590                 case 'date' : da[c.name] = new Date(); break;
5591                 case 'float' : da[c.name] = 0.0; break;
5592                 case 'boolean' : da[c.name] = false; break;
5593                 default : da[c.name] = ""; break;
5594             }
5595             
5596         });
5597         return new this.recordType(Roo.apply(da, d));
5598     }
5599     
5600 };/*
5601  * Based on:
5602  * Ext JS Library 1.1.1
5603  * Copyright(c) 2006-2007, Ext JS, LLC.
5604  *
5605  * Originally Released Under LGPL - original licence link has changed is not relivant.
5606  *
5607  * Fork - LGPL
5608  * <script type="text/javascript">
5609  */
5610
5611 /**
5612  * @class Roo.data.DataProxy
5613  * @extends Roo.data.Observable
5614  * This class is an abstract base class for implementations which provide retrieval of
5615  * unformatted data objects.<br>
5616  * <p>
5617  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5618  * (of the appropriate type which knows how to parse the data object) to provide a block of
5619  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5620  * <p>
5621  * Custom implementations must implement the load method as described in
5622  * {@link Roo.data.HttpProxy#load}.
5623  */
5624 Roo.data.DataProxy = function(){
5625     this.addEvents({
5626         /**
5627          * @event beforeload
5628          * Fires before a network request is made to retrieve a data object.
5629          * @param {Object} This DataProxy object.
5630          * @param {Object} params The params parameter to the load function.
5631          */
5632         beforeload : true,
5633         /**
5634          * @event load
5635          * Fires before the load method's callback is called.
5636          * @param {Object} This DataProxy object.
5637          * @param {Object} o The data object.
5638          * @param {Object} arg The callback argument object passed to the load function.
5639          */
5640         load : true,
5641         /**
5642          * @event loadexception
5643          * Fires if an Exception occurs during data retrieval.
5644          * @param {Object} This DataProxy object.
5645          * @param {Object} o The data object.
5646          * @param {Object} arg The callback argument object passed to the load function.
5647          * @param {Object} e The Exception.
5648          */
5649         loadexception : true
5650     });
5651     Roo.data.DataProxy.superclass.constructor.call(this);
5652 };
5653
5654 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5655
5656     /**
5657      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5658      */
5659 /*
5660  * Based on:
5661  * Ext JS Library 1.1.1
5662  * Copyright(c) 2006-2007, Ext JS, LLC.
5663  *
5664  * Originally Released Under LGPL - original licence link has changed is not relivant.
5665  *
5666  * Fork - LGPL
5667  * <script type="text/javascript">
5668  */
5669 /**
5670  * @class Roo.data.MemoryProxy
5671  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5672  * to the Reader when its load method is called.
5673  * @constructor
5674  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5675  */
5676 Roo.data.MemoryProxy = function(data){
5677     if (data.data) {
5678         data = data.data;
5679     }
5680     Roo.data.MemoryProxy.superclass.constructor.call(this);
5681     this.data = data;
5682 };
5683
5684 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5685     /**
5686      * Load data from the requested source (in this case an in-memory
5687      * data object passed to the constructor), read the data object into
5688      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5689      * process that block using the passed callback.
5690      * @param {Object} params This parameter is not used by the MemoryProxy class.
5691      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5692      * object into a block of Roo.data.Records.
5693      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5694      * The function must be passed <ul>
5695      * <li>The Record block object</li>
5696      * <li>The "arg" argument from the load function</li>
5697      * <li>A boolean success indicator</li>
5698      * </ul>
5699      * @param {Object} scope The scope in which to call the callback
5700      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5701      */
5702     load : function(params, reader, callback, scope, arg){
5703         params = params || {};
5704         var result;
5705         try {
5706             result = reader.readRecords(this.data);
5707         }catch(e){
5708             this.fireEvent("loadexception", this, arg, null, e);
5709             callback.call(scope, null, arg, false);
5710             return;
5711         }
5712         callback.call(scope, result, arg, true);
5713     },
5714     
5715     // private
5716     update : function(params, records){
5717         
5718     }
5719 });/*
5720  * Based on:
5721  * Ext JS Library 1.1.1
5722  * Copyright(c) 2006-2007, Ext JS, LLC.
5723  *
5724  * Originally Released Under LGPL - original licence link has changed is not relivant.
5725  *
5726  * Fork - LGPL
5727  * <script type="text/javascript">
5728  */
5729 /**
5730  * @class Roo.data.HttpProxy
5731  * @extends Roo.data.DataProxy
5732  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5733  * configured to reference a certain URL.<br><br>
5734  * <p>
5735  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5736  * from which the running page was served.<br><br>
5737  * <p>
5738  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5739  * <p>
5740  * Be aware that to enable the browser to parse an XML document, the server must set
5741  * the Content-Type header in the HTTP response to "text/xml".
5742  * @constructor
5743  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5744  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5745  * will be used to make the request.
5746  */
5747 Roo.data.HttpProxy = function(conn){
5748     Roo.data.HttpProxy.superclass.constructor.call(this);
5749     // is conn a conn config or a real conn?
5750     this.conn = conn;
5751     this.useAjax = !conn || !conn.events;
5752   
5753 };
5754
5755 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5756     // thse are take from connection...
5757     
5758     /**
5759      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5760      */
5761     /**
5762      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5763      * extra parameters to each request made by this object. (defaults to undefined)
5764      */
5765     /**
5766      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5767      *  to each request made by this object. (defaults to undefined)
5768      */
5769     /**
5770      * @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)
5771      */
5772     /**
5773      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5774      */
5775      /**
5776      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5777      * @type Boolean
5778      */
5779   
5780
5781     /**
5782      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5783      * @type Boolean
5784      */
5785     /**
5786      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5787      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5788      * a finer-grained basis than the DataProxy events.
5789      */
5790     getConnection : function(){
5791         return this.useAjax ? Roo.Ajax : this.conn;
5792     },
5793
5794     /**
5795      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5796      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5797      * process that block using the passed callback.
5798      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5799      * for the request to the remote server.
5800      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5801      * object into a block of Roo.data.Records.
5802      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5803      * The function must be passed <ul>
5804      * <li>The Record block object</li>
5805      * <li>The "arg" argument from the load function</li>
5806      * <li>A boolean success indicator</li>
5807      * </ul>
5808      * @param {Object} scope The scope in which to call the callback
5809      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5810      */
5811     load : function(params, reader, callback, scope, arg){
5812         if(this.fireEvent("beforeload", this, params) !== false){
5813             var  o = {
5814                 params : params || {},
5815                 request: {
5816                     callback : callback,
5817                     scope : scope,
5818                     arg : arg
5819                 },
5820                 reader: reader,
5821                 callback : this.loadResponse,
5822                 scope: this
5823             };
5824             if(this.useAjax){
5825                 Roo.applyIf(o, this.conn);
5826                 if(this.activeRequest){
5827                     Roo.Ajax.abort(this.activeRequest);
5828                 }
5829                 this.activeRequest = Roo.Ajax.request(o);
5830             }else{
5831                 this.conn.request(o);
5832             }
5833         }else{
5834             callback.call(scope||this, null, arg, false);
5835         }
5836     },
5837
5838     // private
5839     loadResponse : function(o, success, response){
5840         delete this.activeRequest;
5841         if(!success){
5842             this.fireEvent("loadexception", this, o, response);
5843             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5844             return;
5845         }
5846         var result;
5847         try {
5848             result = o.reader.read(response);
5849         }catch(e){
5850             this.fireEvent("loadexception", this, o, response, e);
5851             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5852             return;
5853         }
5854         
5855         this.fireEvent("load", this, o, o.request.arg);
5856         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5857     },
5858
5859     // private
5860     update : function(dataSet){
5861
5862     },
5863
5864     // private
5865     updateResponse : function(dataSet){
5866
5867     }
5868 });/*
5869  * Based on:
5870  * Ext JS Library 1.1.1
5871  * Copyright(c) 2006-2007, Ext JS, LLC.
5872  *
5873  * Originally Released Under LGPL - original licence link has changed is not relivant.
5874  *
5875  * Fork - LGPL
5876  * <script type="text/javascript">
5877  */
5878
5879 /**
5880  * @class Roo.data.ScriptTagProxy
5881  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5882  * other than the originating domain of the running page.<br><br>
5883  * <p>
5884  * <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
5885  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5886  * <p>
5887  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5888  * source code that is used as the source inside a &lt;script> tag.<br><br>
5889  * <p>
5890  * In order for the browser to process the returned data, the server must wrap the data object
5891  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5892  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5893  * depending on whether the callback name was passed:
5894  * <p>
5895  * <pre><code>
5896 boolean scriptTag = false;
5897 String cb = request.getParameter("callback");
5898 if (cb != null) {
5899     scriptTag = true;
5900     response.setContentType("text/javascript");
5901 } else {
5902     response.setContentType("application/x-json");
5903 }
5904 Writer out = response.getWriter();
5905 if (scriptTag) {
5906     out.write(cb + "(");
5907 }
5908 out.print(dataBlock.toJsonString());
5909 if (scriptTag) {
5910     out.write(");");
5911 }
5912 </pre></code>
5913  *
5914  * @constructor
5915  * @param {Object} config A configuration object.
5916  */
5917 Roo.data.ScriptTagProxy = function(config){
5918     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5919     Roo.apply(this, config);
5920     this.head = document.getElementsByTagName("head")[0];
5921 };
5922
5923 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5924
5925 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5926     /**
5927      * @cfg {String} url The URL from which to request the data object.
5928      */
5929     /**
5930      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5931      */
5932     timeout : 30000,
5933     /**
5934      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5935      * the server the name of the callback function set up by the load call to process the returned data object.
5936      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5937      * javascript output which calls this named function passing the data object as its only parameter.
5938      */
5939     callbackParam : "callback",
5940     /**
5941      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5942      * name to the request.
5943      */
5944     nocache : true,
5945
5946     /**
5947      * Load data from the configured URL, read the data object into
5948      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5949      * process that block using the passed callback.
5950      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5951      * for the request to the remote server.
5952      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5953      * object into a block of Roo.data.Records.
5954      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5955      * The function must be passed <ul>
5956      * <li>The Record block object</li>
5957      * <li>The "arg" argument from the load function</li>
5958      * <li>A boolean success indicator</li>
5959      * </ul>
5960      * @param {Object} scope The scope in which to call the callback
5961      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5962      */
5963     load : function(params, reader, callback, scope, arg){
5964         if(this.fireEvent("beforeload", this, params) !== false){
5965
5966             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
5967
5968             var url = this.url;
5969             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
5970             if(this.nocache){
5971                 url += "&_dc=" + (new Date().getTime());
5972             }
5973             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
5974             var trans = {
5975                 id : transId,
5976                 cb : "stcCallback"+transId,
5977                 scriptId : "stcScript"+transId,
5978                 params : params,
5979                 arg : arg,
5980                 url : url,
5981                 callback : callback,
5982                 scope : scope,
5983                 reader : reader
5984             };
5985             var conn = this;
5986
5987             window[trans.cb] = function(o){
5988                 conn.handleResponse(o, trans);
5989             };
5990
5991             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
5992
5993             if(this.autoAbort !== false){
5994                 this.abort();
5995             }
5996
5997             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
5998
5999             var script = document.createElement("script");
6000             script.setAttribute("src", url);
6001             script.setAttribute("type", "text/javascript");
6002             script.setAttribute("id", trans.scriptId);
6003             this.head.appendChild(script);
6004
6005             this.trans = trans;
6006         }else{
6007             callback.call(scope||this, null, arg, false);
6008         }
6009     },
6010
6011     // private
6012     isLoading : function(){
6013         return this.trans ? true : false;
6014     },
6015
6016     /**
6017      * Abort the current server request.
6018      */
6019     abort : function(){
6020         if(this.isLoading()){
6021             this.destroyTrans(this.trans);
6022         }
6023     },
6024
6025     // private
6026     destroyTrans : function(trans, isLoaded){
6027         this.head.removeChild(document.getElementById(trans.scriptId));
6028         clearTimeout(trans.timeoutId);
6029         if(isLoaded){
6030             window[trans.cb] = undefined;
6031             try{
6032                 delete window[trans.cb];
6033             }catch(e){}
6034         }else{
6035             // if hasn't been loaded, wait for load to remove it to prevent script error
6036             window[trans.cb] = function(){
6037                 window[trans.cb] = undefined;
6038                 try{
6039                     delete window[trans.cb];
6040                 }catch(e){}
6041             };
6042         }
6043     },
6044
6045     // private
6046     handleResponse : function(o, trans){
6047         this.trans = false;
6048         this.destroyTrans(trans, true);
6049         var result;
6050         try {
6051             result = trans.reader.readRecords(o);
6052         }catch(e){
6053             this.fireEvent("loadexception", this, o, trans.arg, e);
6054             trans.callback.call(trans.scope||window, null, trans.arg, false);
6055             return;
6056         }
6057         this.fireEvent("load", this, o, trans.arg);
6058         trans.callback.call(trans.scope||window, result, trans.arg, true);
6059     },
6060
6061     // private
6062     handleFailure : function(trans){
6063         this.trans = false;
6064         this.destroyTrans(trans, false);
6065         this.fireEvent("loadexception", this, null, trans.arg);
6066         trans.callback.call(trans.scope||window, null, trans.arg, false);
6067     }
6068 });/*
6069  * Based on:
6070  * Ext JS Library 1.1.1
6071  * Copyright(c) 2006-2007, Ext JS, LLC.
6072  *
6073  * Originally Released Under LGPL - original licence link has changed is not relivant.
6074  *
6075  * Fork - LGPL
6076  * <script type="text/javascript">
6077  */
6078
6079 /**
6080  * @class Roo.data.JsonReader
6081  * @extends Roo.data.DataReader
6082  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6083  * based on mappings in a provided Roo.data.Record constructor.
6084  * 
6085  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6086  * in the reply previously. 
6087  * 
6088  * <p>
6089  * Example code:
6090  * <pre><code>
6091 var RecordDef = Roo.data.Record.create([
6092     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6093     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6094 ]);
6095 var myReader = new Roo.data.JsonReader({
6096     totalProperty: "results",    // The property which contains the total dataset size (optional)
6097     root: "rows",                // The property which contains an Array of row objects
6098     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6099 }, RecordDef);
6100 </code></pre>
6101  * <p>
6102  * This would consume a JSON file like this:
6103  * <pre><code>
6104 { 'results': 2, 'rows': [
6105     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6106     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6107 }
6108 </code></pre>
6109  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6110  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6111  * paged from the remote server.
6112  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6113  * @cfg {String} root name of the property which contains the Array of row objects.
6114  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6115  * @constructor
6116  * Create a new JsonReader
6117  * @param {Object} meta Metadata configuration options
6118  * @param {Object} recordType Either an Array of field definition objects,
6119  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6120  */
6121 Roo.data.JsonReader = function(meta, recordType){
6122     
6123     meta = meta || {};
6124     // set some defaults:
6125     Roo.applyIf(meta, {
6126         totalProperty: 'total',
6127         successProperty : 'success',
6128         root : 'data',
6129         id : 'id'
6130     });
6131     
6132     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6133 };
6134 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6135     
6136     /**
6137      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6138      * Used by Store query builder to append _requestMeta to params.
6139      * 
6140      */
6141     metaFromRemote : false,
6142     /**
6143      * This method is only used by a DataProxy which has retrieved data from a remote server.
6144      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6145      * @return {Object} data A data block which is used by an Roo.data.Store object as
6146      * a cache of Roo.data.Records.
6147      */
6148     read : function(response){
6149         var json = response.responseText;
6150        
6151         var o = /* eval:var:o */ eval("("+json+")");
6152         if(!o) {
6153             throw {message: "JsonReader.read: Json object not found"};
6154         }
6155         
6156         if(o.metaData){
6157             
6158             delete this.ef;
6159             this.metaFromRemote = true;
6160             this.meta = o.metaData;
6161             this.recordType = Roo.data.Record.create(o.metaData.fields);
6162             this.onMetaChange(this.meta, this.recordType, o);
6163         }
6164         return this.readRecords(o);
6165     },
6166
6167     // private function a store will implement
6168     onMetaChange : function(meta, recordType, o){
6169
6170     },
6171
6172     /**
6173          * @ignore
6174          */
6175     simpleAccess: function(obj, subsc) {
6176         return obj[subsc];
6177     },
6178
6179         /**
6180          * @ignore
6181          */
6182     getJsonAccessor: function(){
6183         var re = /[\[\.]/;
6184         return function(expr) {
6185             try {
6186                 return(re.test(expr))
6187                     ? new Function("obj", "return obj." + expr)
6188                     : function(obj){
6189                         return obj[expr];
6190                     };
6191             } catch(e){}
6192             return Roo.emptyFn;
6193         };
6194     }(),
6195
6196     /**
6197      * Create a data block containing Roo.data.Records from an XML document.
6198      * @param {Object} o An object which contains an Array of row objects in the property specified
6199      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6200      * which contains the total size of the dataset.
6201      * @return {Object} data A data block which is used by an Roo.data.Store object as
6202      * a cache of Roo.data.Records.
6203      */
6204     readRecords : function(o){
6205         /**
6206          * After any data loads, the raw JSON data is available for further custom processing.
6207          * @type Object
6208          */
6209         this.jsonData = o;
6210         var s = this.meta, Record = this.recordType,
6211             f = Record.prototype.fields, fi = f.items, fl = f.length;
6212
6213 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6214         if (!this.ef) {
6215             if(s.totalProperty) {
6216                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6217                 }
6218                 if(s.successProperty) {
6219                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6220                 }
6221                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6222                 if (s.id) {
6223                         var g = this.getJsonAccessor(s.id);
6224                         this.getId = function(rec) {
6225                                 var r = g(rec);
6226                                 return (r === undefined || r === "") ? null : r;
6227                         };
6228                 } else {
6229                         this.getId = function(){return null;};
6230                 }
6231             this.ef = [];
6232             for(var jj = 0; jj < fl; jj++){
6233                 f = fi[jj];
6234                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6235                 this.ef[jj] = this.getJsonAccessor(map);
6236             }
6237         }
6238
6239         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6240         if(s.totalProperty){
6241             var vt = parseInt(this.getTotal(o), 10);
6242             if(!isNaN(vt)){
6243                 totalRecords = vt;
6244             }
6245         }
6246         if(s.successProperty){
6247             var vs = this.getSuccess(o);
6248             if(vs === false || vs === 'false'){
6249                 success = false;
6250             }
6251         }
6252         var records = [];
6253             for(var i = 0; i < c; i++){
6254                     var n = root[i];
6255                 var values = {};
6256                 var id = this.getId(n);
6257                 for(var j = 0; j < fl; j++){
6258                     f = fi[j];
6259                 var v = this.ef[j](n);
6260                 if (!f.convert) {
6261                     Roo.log('missing convert for ' + f.name);
6262                     Roo.log(f);
6263                     continue;
6264                 }
6265                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6266                 }
6267                 var record = new Record(values, id);
6268                 record.json = n;
6269                 records[i] = record;
6270             }
6271             return {
6272                 success : success,
6273                 records : records,
6274                 totalRecords : totalRecords
6275             };
6276     }
6277 });/*
6278  * Based on:
6279  * Ext JS Library 1.1.1
6280  * Copyright(c) 2006-2007, Ext JS, LLC.
6281  *
6282  * Originally Released Under LGPL - original licence link has changed is not relivant.
6283  *
6284  * Fork - LGPL
6285  * <script type="text/javascript">
6286  */
6287
6288 /**
6289  * @class Roo.data.XmlReader
6290  * @extends Roo.data.DataReader
6291  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6292  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6293  * <p>
6294  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6295  * header in the HTTP response must be set to "text/xml".</em>
6296  * <p>
6297  * Example code:
6298  * <pre><code>
6299 var RecordDef = Roo.data.Record.create([
6300    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6301    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6302 ]);
6303 var myReader = new Roo.data.XmlReader({
6304    totalRecords: "results", // The element which contains the total dataset size (optional)
6305    record: "row",           // The repeated element which contains row information
6306    id: "id"                 // The element within the row that provides an ID for the record (optional)
6307 }, RecordDef);
6308 </code></pre>
6309  * <p>
6310  * This would consume an XML file like this:
6311  * <pre><code>
6312 &lt;?xml?>
6313 &lt;dataset>
6314  &lt;results>2&lt;/results>
6315  &lt;row>
6316    &lt;id>1&lt;/id>
6317    &lt;name>Bill&lt;/name>
6318    &lt;occupation>Gardener&lt;/occupation>
6319  &lt;/row>
6320  &lt;row>
6321    &lt;id>2&lt;/id>
6322    &lt;name>Ben&lt;/name>
6323    &lt;occupation>Horticulturalist&lt;/occupation>
6324  &lt;/row>
6325 &lt;/dataset>
6326 </code></pre>
6327  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6328  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6329  * paged from the remote server.
6330  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6331  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6332  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6333  * a record identifier value.
6334  * @constructor
6335  * Create a new XmlReader
6336  * @param {Object} meta Metadata configuration options
6337  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6338  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6339  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6340  */
6341 Roo.data.XmlReader = function(meta, recordType){
6342     meta = meta || {};
6343     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6344 };
6345 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6346     /**
6347      * This method is only used by a DataProxy which has retrieved data from a remote server.
6348          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6349          * to contain a method called 'responseXML' that returns an XML document object.
6350      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6351      * a cache of Roo.data.Records.
6352      */
6353     read : function(response){
6354         var doc = response.responseXML;
6355         if(!doc) {
6356             throw {message: "XmlReader.read: XML Document not available"};
6357         }
6358         return this.readRecords(doc);
6359     },
6360
6361     /**
6362      * Create a data block containing Roo.data.Records from an XML document.
6363          * @param {Object} doc A parsed XML document.
6364      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6365      * a cache of Roo.data.Records.
6366      */
6367     readRecords : function(doc){
6368         /**
6369          * After any data loads/reads, the raw XML Document is available for further custom processing.
6370          * @type XMLDocument
6371          */
6372         this.xmlData = doc;
6373         var root = doc.documentElement || doc;
6374         var q = Roo.DomQuery;
6375         var recordType = this.recordType, fields = recordType.prototype.fields;
6376         var sid = this.meta.id;
6377         var totalRecords = 0, success = true;
6378         if(this.meta.totalRecords){
6379             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6380         }
6381         
6382         if(this.meta.success){
6383             var sv = q.selectValue(this.meta.success, root, true);
6384             success = sv !== false && sv !== 'false';
6385         }
6386         var records = [];
6387         var ns = q.select(this.meta.record, root);
6388         for(var i = 0, len = ns.length; i < len; i++) {
6389                 var n = ns[i];
6390                 var values = {};
6391                 var id = sid ? q.selectValue(sid, n) : undefined;
6392                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6393                     var f = fields.items[j];
6394                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6395                     v = f.convert(v);
6396                     values[f.name] = v;
6397                 }
6398                 var record = new recordType(values, id);
6399                 record.node = n;
6400                 records[records.length] = record;
6401             }
6402
6403             return {
6404                 success : success,
6405                 records : records,
6406                 totalRecords : totalRecords || records.length
6407             };
6408     }
6409 });/*
6410  * Based on:
6411  * Ext JS Library 1.1.1
6412  * Copyright(c) 2006-2007, Ext JS, LLC.
6413  *
6414  * Originally Released Under LGPL - original licence link has changed is not relivant.
6415  *
6416  * Fork - LGPL
6417  * <script type="text/javascript">
6418  */
6419
6420 /**
6421  * @class Roo.data.ArrayReader
6422  * @extends Roo.data.DataReader
6423  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6424  * Each element of that Array represents a row of data fields. The
6425  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6426  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6427  * <p>
6428  * Example code:.
6429  * <pre><code>
6430 var RecordDef = Roo.data.Record.create([
6431     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6432     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6433 ]);
6434 var myReader = new Roo.data.ArrayReader({
6435     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6436 }, RecordDef);
6437 </code></pre>
6438  * <p>
6439  * This would consume an Array like this:
6440  * <pre><code>
6441 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6442   </code></pre>
6443  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6444  * @constructor
6445  * Create a new JsonReader
6446  * @param {Object} meta Metadata configuration options.
6447  * @param {Object} recordType Either an Array of field definition objects
6448  * as specified to {@link Roo.data.Record#create},
6449  * or an {@link Roo.data.Record} object
6450  * created using {@link Roo.data.Record#create}.
6451  */
6452 Roo.data.ArrayReader = function(meta, recordType){
6453     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6454 };
6455
6456 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6457     /**
6458      * Create a data block containing Roo.data.Records from an XML document.
6459      * @param {Object} o An Array of row objects which represents the dataset.
6460      * @return {Object} data A data block which is used by an Roo.data.Store object as
6461      * a cache of Roo.data.Records.
6462      */
6463     readRecords : function(o){
6464         var sid = this.meta ? this.meta.id : null;
6465         var recordType = this.recordType, fields = recordType.prototype.fields;
6466         var records = [];
6467         var root = o;
6468             for(var i = 0; i < root.length; i++){
6469                     var n = root[i];
6470                 var values = {};
6471                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6472                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6473                 var f = fields.items[j];
6474                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6475                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6476                 v = f.convert(v);
6477                 values[f.name] = v;
6478             }
6479                 var record = new recordType(values, id);
6480                 record.json = n;
6481                 records[records.length] = record;
6482             }
6483             return {
6484                 records : records,
6485                 totalRecords : records.length
6486             };
6487     }
6488 });/*
6489  * Based on:
6490  * Ext JS Library 1.1.1
6491  * Copyright(c) 2006-2007, Ext JS, LLC.
6492  *
6493  * Originally Released Under LGPL - original licence link has changed is not relivant.
6494  *
6495  * Fork - LGPL
6496  * <script type="text/javascript">
6497  */
6498
6499
6500 /**
6501  * @class Roo.data.Tree
6502  * @extends Roo.util.Observable
6503  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6504  * in the tree have most standard DOM functionality.
6505  * @constructor
6506  * @param {Node} root (optional) The root node
6507  */
6508 Roo.data.Tree = function(root){
6509    this.nodeHash = {};
6510    /**
6511     * The root node for this tree
6512     * @type Node
6513     */
6514    this.root = null;
6515    if(root){
6516        this.setRootNode(root);
6517    }
6518    this.addEvents({
6519        /**
6520         * @event append
6521         * Fires when a new child node is appended to a node in this tree.
6522         * @param {Tree} tree The owner tree
6523         * @param {Node} parent The parent node
6524         * @param {Node} node The newly appended node
6525         * @param {Number} index The index of the newly appended node
6526         */
6527        "append" : true,
6528        /**
6529         * @event remove
6530         * Fires when a child node is removed from a node in this tree.
6531         * @param {Tree} tree The owner tree
6532         * @param {Node} parent The parent node
6533         * @param {Node} node The child node removed
6534         */
6535        "remove" : true,
6536        /**
6537         * @event move
6538         * Fires when a node is moved to a new location in the tree
6539         * @param {Tree} tree The owner tree
6540         * @param {Node} node The node moved
6541         * @param {Node} oldParent The old parent of this node
6542         * @param {Node} newParent The new parent of this node
6543         * @param {Number} index The index it was moved to
6544         */
6545        "move" : true,
6546        /**
6547         * @event insert
6548         * Fires when a new child node is inserted in a node in this tree.
6549         * @param {Tree} tree The owner tree
6550         * @param {Node} parent The parent node
6551         * @param {Node} node The child node inserted
6552         * @param {Node} refNode The child node the node was inserted before
6553         */
6554        "insert" : true,
6555        /**
6556         * @event beforeappend
6557         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6558         * @param {Tree} tree The owner tree
6559         * @param {Node} parent The parent node
6560         * @param {Node} node The child node to be appended
6561         */
6562        "beforeappend" : true,
6563        /**
6564         * @event beforeremove
6565         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6566         * @param {Tree} tree The owner tree
6567         * @param {Node} parent The parent node
6568         * @param {Node} node The child node to be removed
6569         */
6570        "beforeremove" : true,
6571        /**
6572         * @event beforemove
6573         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6574         * @param {Tree} tree The owner tree
6575         * @param {Node} node The node being moved
6576         * @param {Node} oldParent The parent of the node
6577         * @param {Node} newParent The new parent the node is moving to
6578         * @param {Number} index The index it is being moved to
6579         */
6580        "beforemove" : true,
6581        /**
6582         * @event beforeinsert
6583         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6584         * @param {Tree} tree The owner tree
6585         * @param {Node} parent The parent node
6586         * @param {Node} node The child node to be inserted
6587         * @param {Node} refNode The child node the node is being inserted before
6588         */
6589        "beforeinsert" : true
6590    });
6591
6592     Roo.data.Tree.superclass.constructor.call(this);
6593 };
6594
6595 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6596     pathSeparator: "/",
6597
6598     proxyNodeEvent : function(){
6599         return this.fireEvent.apply(this, arguments);
6600     },
6601
6602     /**
6603      * Returns the root node for this tree.
6604      * @return {Node}
6605      */
6606     getRootNode : function(){
6607         return this.root;
6608     },
6609
6610     /**
6611      * Sets the root node for this tree.
6612      * @param {Node} node
6613      * @return {Node}
6614      */
6615     setRootNode : function(node){
6616         this.root = node;
6617         node.ownerTree = this;
6618         node.isRoot = true;
6619         this.registerNode(node);
6620         return node;
6621     },
6622
6623     /**
6624      * Gets a node in this tree by its id.
6625      * @param {String} id
6626      * @return {Node}
6627      */
6628     getNodeById : function(id){
6629         return this.nodeHash[id];
6630     },
6631
6632     registerNode : function(node){
6633         this.nodeHash[node.id] = node;
6634     },
6635
6636     unregisterNode : function(node){
6637         delete this.nodeHash[node.id];
6638     },
6639
6640     toString : function(){
6641         return "[Tree"+(this.id?" "+this.id:"")+"]";
6642     }
6643 });
6644
6645 /**
6646  * @class Roo.data.Node
6647  * @extends Roo.util.Observable
6648  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6649  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6650  * @constructor
6651  * @param {Object} attributes The attributes/config for the node
6652  */
6653 Roo.data.Node = function(attributes){
6654     /**
6655      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6656      * @type {Object}
6657      */
6658     this.attributes = attributes || {};
6659     this.leaf = this.attributes.leaf;
6660     /**
6661      * The node id. @type String
6662      */
6663     this.id = this.attributes.id;
6664     if(!this.id){
6665         this.id = Roo.id(null, "ynode-");
6666         this.attributes.id = this.id;
6667     }
6668     /**
6669      * All child nodes of this node. @type Array
6670      */
6671     this.childNodes = [];
6672     if(!this.childNodes.indexOf){ // indexOf is a must
6673         this.childNodes.indexOf = function(o){
6674             for(var i = 0, len = this.length; i < len; i++){
6675                 if(this[i] == o) {
6676                     return i;
6677                 }
6678             }
6679             return -1;
6680         };
6681     }
6682     /**
6683      * The parent node for this node. @type Node
6684      */
6685     this.parentNode = null;
6686     /**
6687      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6688      */
6689     this.firstChild = null;
6690     /**
6691      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6692      */
6693     this.lastChild = null;
6694     /**
6695      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6696      */
6697     this.previousSibling = null;
6698     /**
6699      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6700      */
6701     this.nextSibling = null;
6702
6703     this.addEvents({
6704        /**
6705         * @event append
6706         * Fires when a new child node is appended
6707         * @param {Tree} tree The owner tree
6708         * @param {Node} this This node
6709         * @param {Node} node The newly appended node
6710         * @param {Number} index The index of the newly appended node
6711         */
6712        "append" : true,
6713        /**
6714         * @event remove
6715         * Fires when a child node is removed
6716         * @param {Tree} tree The owner tree
6717         * @param {Node} this This node
6718         * @param {Node} node The removed node
6719         */
6720        "remove" : true,
6721        /**
6722         * @event move
6723         * Fires when this node is moved to a new location in the tree
6724         * @param {Tree} tree The owner tree
6725         * @param {Node} this This node
6726         * @param {Node} oldParent The old parent of this node
6727         * @param {Node} newParent The new parent of this node
6728         * @param {Number} index The index it was moved to
6729         */
6730        "move" : true,
6731        /**
6732         * @event insert
6733         * Fires when a new child node is inserted.
6734         * @param {Tree} tree The owner tree
6735         * @param {Node} this This node
6736         * @param {Node} node The child node inserted
6737         * @param {Node} refNode The child node the node was inserted before
6738         */
6739        "insert" : true,
6740        /**
6741         * @event beforeappend
6742         * Fires before a new child is appended, return false to cancel the append.
6743         * @param {Tree} tree The owner tree
6744         * @param {Node} this This node
6745         * @param {Node} node The child node to be appended
6746         */
6747        "beforeappend" : true,
6748        /**
6749         * @event beforeremove
6750         * Fires before a child is removed, return false to cancel the remove.
6751         * @param {Tree} tree The owner tree
6752         * @param {Node} this This node
6753         * @param {Node} node The child node to be removed
6754         */
6755        "beforeremove" : true,
6756        /**
6757         * @event beforemove
6758         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6759         * @param {Tree} tree The owner tree
6760         * @param {Node} this This node
6761         * @param {Node} oldParent The parent of this node
6762         * @param {Node} newParent The new parent this node is moving to
6763         * @param {Number} index The index it is being moved to
6764         */
6765        "beforemove" : true,
6766        /**
6767         * @event beforeinsert
6768         * Fires before a new child is inserted, return false to cancel the insert.
6769         * @param {Tree} tree The owner tree
6770         * @param {Node} this This node
6771         * @param {Node} node The child node to be inserted
6772         * @param {Node} refNode The child node the node is being inserted before
6773         */
6774        "beforeinsert" : true
6775    });
6776     this.listeners = this.attributes.listeners;
6777     Roo.data.Node.superclass.constructor.call(this);
6778 };
6779
6780 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6781     fireEvent : function(evtName){
6782         // first do standard event for this node
6783         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6784             return false;
6785         }
6786         // then bubble it up to the tree if the event wasn't cancelled
6787         var ot = this.getOwnerTree();
6788         if(ot){
6789             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6790                 return false;
6791             }
6792         }
6793         return true;
6794     },
6795
6796     /**
6797      * Returns true if this node is a leaf
6798      * @return {Boolean}
6799      */
6800     isLeaf : function(){
6801         return this.leaf === true;
6802     },
6803
6804     // private
6805     setFirstChild : function(node){
6806         this.firstChild = node;
6807     },
6808
6809     //private
6810     setLastChild : function(node){
6811         this.lastChild = node;
6812     },
6813
6814
6815     /**
6816      * Returns true if this node is the last child of its parent
6817      * @return {Boolean}
6818      */
6819     isLast : function(){
6820        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6821     },
6822
6823     /**
6824      * Returns true if this node is the first child of its parent
6825      * @return {Boolean}
6826      */
6827     isFirst : function(){
6828        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6829     },
6830
6831     hasChildNodes : function(){
6832         return !this.isLeaf() && this.childNodes.length > 0;
6833     },
6834
6835     /**
6836      * Insert node(s) as the last child node of this node.
6837      * @param {Node/Array} node The node or Array of nodes to append
6838      * @return {Node} The appended node if single append, or null if an array was passed
6839      */
6840     appendChild : function(node){
6841         var multi = false;
6842         if(node instanceof Array){
6843             multi = node;
6844         }else if(arguments.length > 1){
6845             multi = arguments;
6846         }
6847         // if passed an array or multiple args do them one by one
6848         if(multi){
6849             for(var i = 0, len = multi.length; i < len; i++) {
6850                 this.appendChild(multi[i]);
6851             }
6852         }else{
6853             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6854                 return false;
6855             }
6856             var index = this.childNodes.length;
6857             var oldParent = node.parentNode;
6858             // it's a move, make sure we move it cleanly
6859             if(oldParent){
6860                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6861                     return false;
6862                 }
6863                 oldParent.removeChild(node);
6864             }
6865             index = this.childNodes.length;
6866             if(index == 0){
6867                 this.setFirstChild(node);
6868             }
6869             this.childNodes.push(node);
6870             node.parentNode = this;
6871             var ps = this.childNodes[index-1];
6872             if(ps){
6873                 node.previousSibling = ps;
6874                 ps.nextSibling = node;
6875             }else{
6876                 node.previousSibling = null;
6877             }
6878             node.nextSibling = null;
6879             this.setLastChild(node);
6880             node.setOwnerTree(this.getOwnerTree());
6881             this.fireEvent("append", this.ownerTree, this, node, index);
6882             if(oldParent){
6883                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6884             }
6885             return node;
6886         }
6887     },
6888
6889     /**
6890      * Removes a child node from this node.
6891      * @param {Node} node The node to remove
6892      * @return {Node} The removed node
6893      */
6894     removeChild : function(node){
6895         var index = this.childNodes.indexOf(node);
6896         if(index == -1){
6897             return false;
6898         }
6899         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6900             return false;
6901         }
6902
6903         // remove it from childNodes collection
6904         this.childNodes.splice(index, 1);
6905
6906         // update siblings
6907         if(node.previousSibling){
6908             node.previousSibling.nextSibling = node.nextSibling;
6909         }
6910         if(node.nextSibling){
6911             node.nextSibling.previousSibling = node.previousSibling;
6912         }
6913
6914         // update child refs
6915         if(this.firstChild == node){
6916             this.setFirstChild(node.nextSibling);
6917         }
6918         if(this.lastChild == node){
6919             this.setLastChild(node.previousSibling);
6920         }
6921
6922         node.setOwnerTree(null);
6923         // clear any references from the node
6924         node.parentNode = null;
6925         node.previousSibling = null;
6926         node.nextSibling = null;
6927         this.fireEvent("remove", this.ownerTree, this, node);
6928         return node;
6929     },
6930
6931     /**
6932      * Inserts the first node before the second node in this nodes childNodes collection.
6933      * @param {Node} node The node to insert
6934      * @param {Node} refNode The node to insert before (if null the node is appended)
6935      * @return {Node} The inserted node
6936      */
6937     insertBefore : function(node, refNode){
6938         if(!refNode){ // like standard Dom, refNode can be null for append
6939             return this.appendChild(node);
6940         }
6941         // nothing to do
6942         if(node == refNode){
6943             return false;
6944         }
6945
6946         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6947             return false;
6948         }
6949         var index = this.childNodes.indexOf(refNode);
6950         var oldParent = node.parentNode;
6951         var refIndex = index;
6952
6953         // when moving internally, indexes will change after remove
6954         if(oldParent == this && this.childNodes.indexOf(node) < index){
6955             refIndex--;
6956         }
6957
6958         // it's a move, make sure we move it cleanly
6959         if(oldParent){
6960             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
6961                 return false;
6962             }
6963             oldParent.removeChild(node);
6964         }
6965         if(refIndex == 0){
6966             this.setFirstChild(node);
6967         }
6968         this.childNodes.splice(refIndex, 0, node);
6969         node.parentNode = this;
6970         var ps = this.childNodes[refIndex-1];
6971         if(ps){
6972             node.previousSibling = ps;
6973             ps.nextSibling = node;
6974         }else{
6975             node.previousSibling = null;
6976         }
6977         node.nextSibling = refNode;
6978         refNode.previousSibling = node;
6979         node.setOwnerTree(this.getOwnerTree());
6980         this.fireEvent("insert", this.ownerTree, this, node, refNode);
6981         if(oldParent){
6982             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
6983         }
6984         return node;
6985     },
6986
6987     /**
6988      * Returns the child node at the specified index.
6989      * @param {Number} index
6990      * @return {Node}
6991      */
6992     item : function(index){
6993         return this.childNodes[index];
6994     },
6995
6996     /**
6997      * Replaces one child node in this node with another.
6998      * @param {Node} newChild The replacement node
6999      * @param {Node} oldChild The node to replace
7000      * @return {Node} The replaced node
7001      */
7002     replaceChild : function(newChild, oldChild){
7003         this.insertBefore(newChild, oldChild);
7004         this.removeChild(oldChild);
7005         return oldChild;
7006     },
7007
7008     /**
7009      * Returns the index of a child node
7010      * @param {Node} node
7011      * @return {Number} The index of the node or -1 if it was not found
7012      */
7013     indexOf : function(child){
7014         return this.childNodes.indexOf(child);
7015     },
7016
7017     /**
7018      * Returns the tree this node is in.
7019      * @return {Tree}
7020      */
7021     getOwnerTree : function(){
7022         // if it doesn't have one, look for one
7023         if(!this.ownerTree){
7024             var p = this;
7025             while(p){
7026                 if(p.ownerTree){
7027                     this.ownerTree = p.ownerTree;
7028                     break;
7029                 }
7030                 p = p.parentNode;
7031             }
7032         }
7033         return this.ownerTree;
7034     },
7035
7036     /**
7037      * Returns depth of this node (the root node has a depth of 0)
7038      * @return {Number}
7039      */
7040     getDepth : function(){
7041         var depth = 0;
7042         var p = this;
7043         while(p.parentNode){
7044             ++depth;
7045             p = p.parentNode;
7046         }
7047         return depth;
7048     },
7049
7050     // private
7051     setOwnerTree : function(tree){
7052         // if it's move, we need to update everyone
7053         if(tree != this.ownerTree){
7054             if(this.ownerTree){
7055                 this.ownerTree.unregisterNode(this);
7056             }
7057             this.ownerTree = tree;
7058             var cs = this.childNodes;
7059             for(var i = 0, len = cs.length; i < len; i++) {
7060                 cs[i].setOwnerTree(tree);
7061             }
7062             if(tree){
7063                 tree.registerNode(this);
7064             }
7065         }
7066     },
7067
7068     /**
7069      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7070      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7071      * @return {String} The path
7072      */
7073     getPath : function(attr){
7074         attr = attr || "id";
7075         var p = this.parentNode;
7076         var b = [this.attributes[attr]];
7077         while(p){
7078             b.unshift(p.attributes[attr]);
7079             p = p.parentNode;
7080         }
7081         var sep = this.getOwnerTree().pathSeparator;
7082         return sep + b.join(sep);
7083     },
7084
7085     /**
7086      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7087      * function call will be the scope provided or the current node. The arguments to the function
7088      * will be the args provided or the current node. If the function returns false at any point,
7089      * the bubble is stopped.
7090      * @param {Function} fn The function to call
7091      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7092      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7093      */
7094     bubble : function(fn, scope, args){
7095         var p = this;
7096         while(p){
7097             if(fn.call(scope || p, args || p) === false){
7098                 break;
7099             }
7100             p = p.parentNode;
7101         }
7102     },
7103
7104     /**
7105      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7106      * function call will be the scope provided or the current node. The arguments to the function
7107      * will be the args provided or the current node. If the function returns false at any point,
7108      * the cascade is stopped on that branch.
7109      * @param {Function} fn The function to call
7110      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7111      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7112      */
7113     cascade : function(fn, scope, args){
7114         if(fn.call(scope || this, args || this) !== false){
7115             var cs = this.childNodes;
7116             for(var i = 0, len = cs.length; i < len; i++) {
7117                 cs[i].cascade(fn, scope, args);
7118             }
7119         }
7120     },
7121
7122     /**
7123      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7124      * function call will be the scope provided or the current node. The arguments to the function
7125      * will be the args provided or the current node. If the function returns false at any point,
7126      * the iteration stops.
7127      * @param {Function} fn The function to call
7128      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7129      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7130      */
7131     eachChild : function(fn, scope, args){
7132         var cs = this.childNodes;
7133         for(var i = 0, len = cs.length; i < len; i++) {
7134                 if(fn.call(scope || this, args || cs[i]) === false){
7135                     break;
7136                 }
7137         }
7138     },
7139
7140     /**
7141      * Finds the first child that has the attribute with the specified value.
7142      * @param {String} attribute The attribute name
7143      * @param {Mixed} value The value to search for
7144      * @return {Node} The found child or null if none was found
7145      */
7146     findChild : function(attribute, value){
7147         var cs = this.childNodes;
7148         for(var i = 0, len = cs.length; i < len; i++) {
7149                 if(cs[i].attributes[attribute] == value){
7150                     return cs[i];
7151                 }
7152         }
7153         return null;
7154     },
7155
7156     /**
7157      * Finds the first child by a custom function. The child matches if the function passed
7158      * returns true.
7159      * @param {Function} fn
7160      * @param {Object} scope (optional)
7161      * @return {Node} The found child or null if none was found
7162      */
7163     findChildBy : function(fn, scope){
7164         var cs = this.childNodes;
7165         for(var i = 0, len = cs.length; i < len; i++) {
7166                 if(fn.call(scope||cs[i], cs[i]) === true){
7167                     return cs[i];
7168                 }
7169         }
7170         return null;
7171     },
7172
7173     /**
7174      * Sorts this nodes children using the supplied sort function
7175      * @param {Function} fn
7176      * @param {Object} scope (optional)
7177      */
7178     sort : function(fn, scope){
7179         var cs = this.childNodes;
7180         var len = cs.length;
7181         if(len > 0){
7182             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7183             cs.sort(sortFn);
7184             for(var i = 0; i < len; i++){
7185                 var n = cs[i];
7186                 n.previousSibling = cs[i-1];
7187                 n.nextSibling = cs[i+1];
7188                 if(i == 0){
7189                     this.setFirstChild(n);
7190                 }
7191                 if(i == len-1){
7192                     this.setLastChild(n);
7193                 }
7194             }
7195         }
7196     },
7197
7198     /**
7199      * Returns true if this node is an ancestor (at any point) of the passed node.
7200      * @param {Node} node
7201      * @return {Boolean}
7202      */
7203     contains : function(node){
7204         return node.isAncestor(this);
7205     },
7206
7207     /**
7208      * Returns true if the passed node is an ancestor (at any point) of this node.
7209      * @param {Node} node
7210      * @return {Boolean}
7211      */
7212     isAncestor : function(node){
7213         var p = this.parentNode;
7214         while(p){
7215             if(p == node){
7216                 return true;
7217             }
7218             p = p.parentNode;
7219         }
7220         return false;
7221     },
7222
7223     toString : function(){
7224         return "[Node"+(this.id?" "+this.id:"")+"]";
7225     }
7226 });/*
7227  * Based on:
7228  * Ext JS Library 1.1.1
7229  * Copyright(c) 2006-2007, Ext JS, LLC.
7230  *
7231  * Originally Released Under LGPL - original licence link has changed is not relivant.
7232  *
7233  * Fork - LGPL
7234  * <script type="text/javascript">
7235  */
7236  
7237
7238 /**
7239  * @class Roo.ComponentMgr
7240  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7241  * @singleton
7242  */
7243 Roo.ComponentMgr = function(){
7244     var all = new Roo.util.MixedCollection();
7245
7246     return {
7247         /**
7248          * Registers a component.
7249          * @param {Roo.Component} c The component
7250          */
7251         register : function(c){
7252             all.add(c);
7253         },
7254
7255         /**
7256          * Unregisters a component.
7257          * @param {Roo.Component} c The component
7258          */
7259         unregister : function(c){
7260             all.remove(c);
7261         },
7262
7263         /**
7264          * Returns a component by id
7265          * @param {String} id The component id
7266          */
7267         get : function(id){
7268             return all.get(id);
7269         },
7270
7271         /**
7272          * Registers a function that will be called when a specified component is added to ComponentMgr
7273          * @param {String} id The component id
7274          * @param {Funtction} fn The callback function
7275          * @param {Object} scope The scope of the callback
7276          */
7277         onAvailable : function(id, fn, scope){
7278             all.on("add", function(index, o){
7279                 if(o.id == id){
7280                     fn.call(scope || o, o);
7281                     all.un("add", fn, scope);
7282                 }
7283             });
7284         }
7285     };
7286 }();/*
7287  * Based on:
7288  * Ext JS Library 1.1.1
7289  * Copyright(c) 2006-2007, Ext JS, LLC.
7290  *
7291  * Originally Released Under LGPL - original licence link has changed is not relivant.
7292  *
7293  * Fork - LGPL
7294  * <script type="text/javascript">
7295  */
7296  
7297 /**
7298  * @class Roo.Component
7299  * @extends Roo.util.Observable
7300  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7301  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7302  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7303  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7304  * All visual components (widgets) that require rendering into a layout should subclass Component.
7305  * @constructor
7306  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7307  * 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
7308  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7309  */
7310 Roo.Component = function(config){
7311     config = config || {};
7312     if(config.tagName || config.dom || typeof config == "string"){ // element object
7313         config = {el: config, id: config.id || config};
7314     }
7315     this.initialConfig = config;
7316
7317     Roo.apply(this, config);
7318     this.addEvents({
7319         /**
7320          * @event disable
7321          * Fires after the component is disabled.
7322              * @param {Roo.Component} this
7323              */
7324         disable : true,
7325         /**
7326          * @event enable
7327          * Fires after the component is enabled.
7328              * @param {Roo.Component} this
7329              */
7330         enable : true,
7331         /**
7332          * @event beforeshow
7333          * Fires before the component is shown.  Return false to stop the show.
7334              * @param {Roo.Component} this
7335              */
7336         beforeshow : true,
7337         /**
7338          * @event show
7339          * Fires after the component is shown.
7340              * @param {Roo.Component} this
7341              */
7342         show : true,
7343         /**
7344          * @event beforehide
7345          * Fires before the component is hidden. Return false to stop the hide.
7346              * @param {Roo.Component} this
7347              */
7348         beforehide : true,
7349         /**
7350          * @event hide
7351          * Fires after the component is hidden.
7352              * @param {Roo.Component} this
7353              */
7354         hide : true,
7355         /**
7356          * @event beforerender
7357          * Fires before the component is rendered. Return false to stop the render.
7358              * @param {Roo.Component} this
7359              */
7360         beforerender : true,
7361         /**
7362          * @event render
7363          * Fires after the component is rendered.
7364              * @param {Roo.Component} this
7365              */
7366         render : true,
7367         /**
7368          * @event beforedestroy
7369          * Fires before the component is destroyed. Return false to stop the destroy.
7370              * @param {Roo.Component} this
7371              */
7372         beforedestroy : true,
7373         /**
7374          * @event destroy
7375          * Fires after the component is destroyed.
7376              * @param {Roo.Component} this
7377              */
7378         destroy : true
7379     });
7380     if(!this.id){
7381         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7382     }
7383     Roo.ComponentMgr.register(this);
7384     Roo.Component.superclass.constructor.call(this);
7385     this.initComponent();
7386     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7387         this.render(this.renderTo);
7388         delete this.renderTo;
7389     }
7390 };
7391
7392 // private
7393 Roo.Component.AUTO_ID = 1000;
7394
7395 Roo.extend(Roo.Component, Roo.util.Observable, {
7396     /**
7397      * @property {Boolean} hidden
7398      * true if this component is hidden. Read-only.
7399      */
7400     hidden : false,
7401     /**
7402      * true if this component is disabled. Read-only.
7403      */
7404     disabled : false,
7405     /**
7406      * true if this component has been rendered. Read-only.
7407      */
7408     rendered : false,
7409     
7410     /** @cfg {String} disableClass
7411      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7412      */
7413     disabledClass : "x-item-disabled",
7414         /** @cfg {Boolean} allowDomMove
7415          * Whether the component can move the Dom node when rendering (defaults to true).
7416          */
7417     allowDomMove : true,
7418     /** @cfg {String} hideMode
7419      * How this component should hidden. Supported values are
7420      * "visibility" (css visibility), "offsets" (negative offset position) and
7421      * "display" (css display) - defaults to "display".
7422      */
7423     hideMode: 'display',
7424
7425     // private
7426     ctype : "Roo.Component",
7427
7428     /** @cfg {String} actionMode 
7429      * which property holds the element that used for  hide() / show() / disable() / enable()
7430      * default is 'el' 
7431      */
7432     actionMode : "el",
7433
7434     // private
7435     getActionEl : function(){
7436         return this[this.actionMode];
7437     },
7438
7439     initComponent : Roo.emptyFn,
7440     /**
7441      * If this is a lazy rendering component, render it to its container element.
7442      * @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.
7443      */
7444     render : function(container, position){
7445         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7446             if(!container && this.el){
7447                 this.el = Roo.get(this.el);
7448                 container = this.el.dom.parentNode;
7449                 this.allowDomMove = false;
7450             }
7451             this.container = Roo.get(container);
7452             this.rendered = true;
7453             if(position !== undefined){
7454                 if(typeof position == 'number'){
7455                     position = this.container.dom.childNodes[position];
7456                 }else{
7457                     position = Roo.getDom(position);
7458                 }
7459             }
7460             this.onRender(this.container, position || null);
7461             if(this.cls){
7462                 this.el.addClass(this.cls);
7463                 delete this.cls;
7464             }
7465             if(this.style){
7466                 this.el.applyStyles(this.style);
7467                 delete this.style;
7468             }
7469             this.fireEvent("render", this);
7470             this.afterRender(this.container);
7471             if(this.hidden){
7472                 this.hide();
7473             }
7474             if(this.disabled){
7475                 this.disable();
7476             }
7477         }
7478         return this;
7479     },
7480
7481     // private
7482     // default function is not really useful
7483     onRender : function(ct, position){
7484         if(this.el){
7485             this.el = Roo.get(this.el);
7486             if(this.allowDomMove !== false){
7487                 ct.dom.insertBefore(this.el.dom, position);
7488             }
7489         }
7490     },
7491
7492     // private
7493     getAutoCreate : function(){
7494         var cfg = typeof this.autoCreate == "object" ?
7495                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7496         if(this.id && !cfg.id){
7497             cfg.id = this.id;
7498         }
7499         return cfg;
7500     },
7501
7502     // private
7503     afterRender : Roo.emptyFn,
7504
7505     /**
7506      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7507      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7508      */
7509     destroy : function(){
7510         if(this.fireEvent("beforedestroy", this) !== false){
7511             this.purgeListeners();
7512             this.beforeDestroy();
7513             if(this.rendered){
7514                 this.el.removeAllListeners();
7515                 this.el.remove();
7516                 if(this.actionMode == "container"){
7517                     this.container.remove();
7518                 }
7519             }
7520             this.onDestroy();
7521             Roo.ComponentMgr.unregister(this);
7522             this.fireEvent("destroy", this);
7523         }
7524     },
7525
7526         // private
7527     beforeDestroy : function(){
7528
7529     },
7530
7531         // private
7532         onDestroy : function(){
7533
7534     },
7535
7536     /**
7537      * Returns the underlying {@link Roo.Element}.
7538      * @return {Roo.Element} The element
7539      */
7540     getEl : function(){
7541         return this.el;
7542     },
7543
7544     /**
7545      * Returns the id of this component.
7546      * @return {String}
7547      */
7548     getId : function(){
7549         return this.id;
7550     },
7551
7552     /**
7553      * Try to focus this component.
7554      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7555      * @return {Roo.Component} this
7556      */
7557     focus : function(selectText){
7558         if(this.rendered){
7559             this.el.focus();
7560             if(selectText === true){
7561                 this.el.dom.select();
7562             }
7563         }
7564         return this;
7565     },
7566
7567     // private
7568     blur : function(){
7569         if(this.rendered){
7570             this.el.blur();
7571         }
7572         return this;
7573     },
7574
7575     /**
7576      * Disable this component.
7577      * @return {Roo.Component} this
7578      */
7579     disable : function(){
7580         if(this.rendered){
7581             this.onDisable();
7582         }
7583         this.disabled = true;
7584         this.fireEvent("disable", this);
7585         return this;
7586     },
7587
7588         // private
7589     onDisable : function(){
7590         this.getActionEl().addClass(this.disabledClass);
7591         this.el.dom.disabled = true;
7592     },
7593
7594     /**
7595      * Enable this component.
7596      * @return {Roo.Component} this
7597      */
7598     enable : function(){
7599         if(this.rendered){
7600             this.onEnable();
7601         }
7602         this.disabled = false;
7603         this.fireEvent("enable", this);
7604         return this;
7605     },
7606
7607         // private
7608     onEnable : function(){
7609         this.getActionEl().removeClass(this.disabledClass);
7610         this.el.dom.disabled = false;
7611     },
7612
7613     /**
7614      * Convenience function for setting disabled/enabled by boolean.
7615      * @param {Boolean} disabled
7616      */
7617     setDisabled : function(disabled){
7618         this[disabled ? "disable" : "enable"]();
7619     },
7620
7621     /**
7622      * Show this component.
7623      * @return {Roo.Component} this
7624      */
7625     show: function(){
7626         if(this.fireEvent("beforeshow", this) !== false){
7627             this.hidden = false;
7628             if(this.rendered){
7629                 this.onShow();
7630             }
7631             this.fireEvent("show", this);
7632         }
7633         return this;
7634     },
7635
7636     // private
7637     onShow : function(){
7638         var ae = this.getActionEl();
7639         if(this.hideMode == 'visibility'){
7640             ae.dom.style.visibility = "visible";
7641         }else if(this.hideMode == 'offsets'){
7642             ae.removeClass('x-hidden');
7643         }else{
7644             ae.dom.style.display = "";
7645         }
7646     },
7647
7648     /**
7649      * Hide this component.
7650      * @return {Roo.Component} this
7651      */
7652     hide: function(){
7653         if(this.fireEvent("beforehide", this) !== false){
7654             this.hidden = true;
7655             if(this.rendered){
7656                 this.onHide();
7657             }
7658             this.fireEvent("hide", this);
7659         }
7660         return this;
7661     },
7662
7663     // private
7664     onHide : function(){
7665         var ae = this.getActionEl();
7666         if(this.hideMode == 'visibility'){
7667             ae.dom.style.visibility = "hidden";
7668         }else if(this.hideMode == 'offsets'){
7669             ae.addClass('x-hidden');
7670         }else{
7671             ae.dom.style.display = "none";
7672         }
7673     },
7674
7675     /**
7676      * Convenience function to hide or show this component by boolean.
7677      * @param {Boolean} visible True to show, false to hide
7678      * @return {Roo.Component} this
7679      */
7680     setVisible: function(visible){
7681         if(visible) {
7682             this.show();
7683         }else{
7684             this.hide();
7685         }
7686         return this;
7687     },
7688
7689     /**
7690      * Returns true if this component is visible.
7691      */
7692     isVisible : function(){
7693         return this.getActionEl().isVisible();
7694     },
7695
7696     cloneConfig : function(overrides){
7697         overrides = overrides || {};
7698         var id = overrides.id || Roo.id();
7699         var cfg = Roo.applyIf(overrides, this.initialConfig);
7700         cfg.id = id; // prevent dup id
7701         return new this.constructor(cfg);
7702     }
7703 });/*
7704  * Based on:
7705  * Ext JS Library 1.1.1
7706  * Copyright(c) 2006-2007, Ext JS, LLC.
7707  *
7708  * Originally Released Under LGPL - original licence link has changed is not relivant.
7709  *
7710  * Fork - LGPL
7711  * <script type="text/javascript">
7712  */
7713  (function(){ 
7714 /**
7715  * @class Roo.Layer
7716  * @extends Roo.Element
7717  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7718  * automatic maintaining of shadow/shim positions.
7719  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7720  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7721  * you can pass a string with a CSS class name. False turns off the shadow.
7722  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7723  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7724  * @cfg {String} cls CSS class to add to the element
7725  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7726  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7727  * @constructor
7728  * @param {Object} config An object with config options.
7729  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7730  */
7731
7732 Roo.Layer = function(config, existingEl){
7733     config = config || {};
7734     var dh = Roo.DomHelper;
7735     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7736     if(existingEl){
7737         this.dom = Roo.getDom(existingEl);
7738     }
7739     if(!this.dom){
7740         var o = config.dh || {tag: "div", cls: "x-layer"};
7741         this.dom = dh.append(pel, o);
7742     }
7743     if(config.cls){
7744         this.addClass(config.cls);
7745     }
7746     this.constrain = config.constrain !== false;
7747     this.visibilityMode = Roo.Element.VISIBILITY;
7748     if(config.id){
7749         this.id = this.dom.id = config.id;
7750     }else{
7751         this.id = Roo.id(this.dom);
7752     }
7753     this.zindex = config.zindex || this.getZIndex();
7754     this.position("absolute", this.zindex);
7755     if(config.shadow){
7756         this.shadowOffset = config.shadowOffset || 4;
7757         this.shadow = new Roo.Shadow({
7758             offset : this.shadowOffset,
7759             mode : config.shadow
7760         });
7761     }else{
7762         this.shadowOffset = 0;
7763     }
7764     this.useShim = config.shim !== false && Roo.useShims;
7765     this.useDisplay = config.useDisplay;
7766     this.hide();
7767 };
7768
7769 var supr = Roo.Element.prototype;
7770
7771 // shims are shared among layer to keep from having 100 iframes
7772 var shims = [];
7773
7774 Roo.extend(Roo.Layer, Roo.Element, {
7775
7776     getZIndex : function(){
7777         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7778     },
7779
7780     getShim : function(){
7781         if(!this.useShim){
7782             return null;
7783         }
7784         if(this.shim){
7785             return this.shim;
7786         }
7787         var shim = shims.shift();
7788         if(!shim){
7789             shim = this.createShim();
7790             shim.enableDisplayMode('block');
7791             shim.dom.style.display = 'none';
7792             shim.dom.style.visibility = 'visible';
7793         }
7794         var pn = this.dom.parentNode;
7795         if(shim.dom.parentNode != pn){
7796             pn.insertBefore(shim.dom, this.dom);
7797         }
7798         shim.setStyle('z-index', this.getZIndex()-2);
7799         this.shim = shim;
7800         return shim;
7801     },
7802
7803     hideShim : function(){
7804         if(this.shim){
7805             this.shim.setDisplayed(false);
7806             shims.push(this.shim);
7807             delete this.shim;
7808         }
7809     },
7810
7811     disableShadow : function(){
7812         if(this.shadow){
7813             this.shadowDisabled = true;
7814             this.shadow.hide();
7815             this.lastShadowOffset = this.shadowOffset;
7816             this.shadowOffset = 0;
7817         }
7818     },
7819
7820     enableShadow : function(show){
7821         if(this.shadow){
7822             this.shadowDisabled = false;
7823             this.shadowOffset = this.lastShadowOffset;
7824             delete this.lastShadowOffset;
7825             if(show){
7826                 this.sync(true);
7827             }
7828         }
7829     },
7830
7831     // private
7832     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7833     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7834     sync : function(doShow){
7835         var sw = this.shadow;
7836         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7837             var sh = this.getShim();
7838
7839             var w = this.getWidth(),
7840                 h = this.getHeight();
7841
7842             var l = this.getLeft(true),
7843                 t = this.getTop(true);
7844
7845             if(sw && !this.shadowDisabled){
7846                 if(doShow && !sw.isVisible()){
7847                     sw.show(this);
7848                 }else{
7849                     sw.realign(l, t, w, h);
7850                 }
7851                 if(sh){
7852                     if(doShow){
7853                        sh.show();
7854                     }
7855                     // fit the shim behind the shadow, so it is shimmed too
7856                     var a = sw.adjusts, s = sh.dom.style;
7857                     s.left = (Math.min(l, l+a.l))+"px";
7858                     s.top = (Math.min(t, t+a.t))+"px";
7859                     s.width = (w+a.w)+"px";
7860                     s.height = (h+a.h)+"px";
7861                 }
7862             }else if(sh){
7863                 if(doShow){
7864                    sh.show();
7865                 }
7866                 sh.setSize(w, h);
7867                 sh.setLeftTop(l, t);
7868             }
7869             
7870         }
7871     },
7872
7873     // private
7874     destroy : function(){
7875         this.hideShim();
7876         if(this.shadow){
7877             this.shadow.hide();
7878         }
7879         this.removeAllListeners();
7880         var pn = this.dom.parentNode;
7881         if(pn){
7882             pn.removeChild(this.dom);
7883         }
7884         Roo.Element.uncache(this.id);
7885     },
7886
7887     remove : function(){
7888         this.destroy();
7889     },
7890
7891     // private
7892     beginUpdate : function(){
7893         this.updating = true;
7894     },
7895
7896     // private
7897     endUpdate : function(){
7898         this.updating = false;
7899         this.sync(true);
7900     },
7901
7902     // private
7903     hideUnders : function(negOffset){
7904         if(this.shadow){
7905             this.shadow.hide();
7906         }
7907         this.hideShim();
7908     },
7909
7910     // private
7911     constrainXY : function(){
7912         if(this.constrain){
7913             var vw = Roo.lib.Dom.getViewWidth(),
7914                 vh = Roo.lib.Dom.getViewHeight();
7915             var s = Roo.get(document).getScroll();
7916
7917             var xy = this.getXY();
7918             var x = xy[0], y = xy[1];   
7919             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7920             // only move it if it needs it
7921             var moved = false;
7922             // first validate right/bottom
7923             if((x + w) > vw+s.left){
7924                 x = vw - w - this.shadowOffset;
7925                 moved = true;
7926             }
7927             if((y + h) > vh+s.top){
7928                 y = vh - h - this.shadowOffset;
7929                 moved = true;
7930             }
7931             // then make sure top/left isn't negative
7932             if(x < s.left){
7933                 x = s.left;
7934                 moved = true;
7935             }
7936             if(y < s.top){
7937                 y = s.top;
7938                 moved = true;
7939             }
7940             if(moved){
7941                 if(this.avoidY){
7942                     var ay = this.avoidY;
7943                     if(y <= ay && (y+h) >= ay){
7944                         y = ay-h-5;   
7945                     }
7946                 }
7947                 xy = [x, y];
7948                 this.storeXY(xy);
7949                 supr.setXY.call(this, xy);
7950                 this.sync();
7951             }
7952         }
7953     },
7954
7955     isVisible : function(){
7956         return this.visible;    
7957     },
7958
7959     // private
7960     showAction : function(){
7961         this.visible = true; // track visibility to prevent getStyle calls
7962         if(this.useDisplay === true){
7963             this.setDisplayed("");
7964         }else if(this.lastXY){
7965             supr.setXY.call(this, this.lastXY);
7966         }else if(this.lastLT){
7967             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7968         }
7969     },
7970
7971     // private
7972     hideAction : function(){
7973         this.visible = false;
7974         if(this.useDisplay === true){
7975             this.setDisplayed(false);
7976         }else{
7977             this.setLeftTop(-10000,-10000);
7978         }
7979     },
7980
7981     // overridden Element method
7982     setVisible : function(v, a, d, c, e){
7983         if(v){
7984             this.showAction();
7985         }
7986         if(a && v){
7987             var cb = function(){
7988                 this.sync(true);
7989                 if(c){
7990                     c();
7991                 }
7992             }.createDelegate(this);
7993             supr.setVisible.call(this, true, true, d, cb, e);
7994         }else{
7995             if(!v){
7996                 this.hideUnders(true);
7997             }
7998             var cb = c;
7999             if(a){
8000                 cb = function(){
8001                     this.hideAction();
8002                     if(c){
8003                         c();
8004                     }
8005                 }.createDelegate(this);
8006             }
8007             supr.setVisible.call(this, v, a, d, cb, e);
8008             if(v){
8009                 this.sync(true);
8010             }else if(!a){
8011                 this.hideAction();
8012             }
8013         }
8014     },
8015
8016     storeXY : function(xy){
8017         delete this.lastLT;
8018         this.lastXY = xy;
8019     },
8020
8021     storeLeftTop : function(left, top){
8022         delete this.lastXY;
8023         this.lastLT = [left, top];
8024     },
8025
8026     // private
8027     beforeFx : function(){
8028         this.beforeAction();
8029         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
8030     },
8031
8032     // private
8033     afterFx : function(){
8034         Roo.Layer.superclass.afterFx.apply(this, arguments);
8035         this.sync(this.isVisible());
8036     },
8037
8038     // private
8039     beforeAction : function(){
8040         if(!this.updating && this.shadow){
8041             this.shadow.hide();
8042         }
8043     },
8044
8045     // overridden Element method
8046     setLeft : function(left){
8047         this.storeLeftTop(left, this.getTop(true));
8048         supr.setLeft.apply(this, arguments);
8049         this.sync();
8050     },
8051
8052     setTop : function(top){
8053         this.storeLeftTop(this.getLeft(true), top);
8054         supr.setTop.apply(this, arguments);
8055         this.sync();
8056     },
8057
8058     setLeftTop : function(left, top){
8059         this.storeLeftTop(left, top);
8060         supr.setLeftTop.apply(this, arguments);
8061         this.sync();
8062     },
8063
8064     setXY : function(xy, a, d, c, e){
8065         this.fixDisplay();
8066         this.beforeAction();
8067         this.storeXY(xy);
8068         var cb = this.createCB(c);
8069         supr.setXY.call(this, xy, a, d, cb, e);
8070         if(!a){
8071             cb();
8072         }
8073     },
8074
8075     // private
8076     createCB : function(c){
8077         var el = this;
8078         return function(){
8079             el.constrainXY();
8080             el.sync(true);
8081             if(c){
8082                 c();
8083             }
8084         };
8085     },
8086
8087     // overridden Element method
8088     setX : function(x, a, d, c, e){
8089         this.setXY([x, this.getY()], a, d, c, e);
8090     },
8091
8092     // overridden Element method
8093     setY : function(y, a, d, c, e){
8094         this.setXY([this.getX(), y], a, d, c, e);
8095     },
8096
8097     // overridden Element method
8098     setSize : function(w, h, a, d, c, e){
8099         this.beforeAction();
8100         var cb = this.createCB(c);
8101         supr.setSize.call(this, w, h, a, d, cb, e);
8102         if(!a){
8103             cb();
8104         }
8105     },
8106
8107     // overridden Element method
8108     setWidth : function(w, a, d, c, e){
8109         this.beforeAction();
8110         var cb = this.createCB(c);
8111         supr.setWidth.call(this, w, a, d, cb, e);
8112         if(!a){
8113             cb();
8114         }
8115     },
8116
8117     // overridden Element method
8118     setHeight : function(h, a, d, c, e){
8119         this.beforeAction();
8120         var cb = this.createCB(c);
8121         supr.setHeight.call(this, h, a, d, cb, e);
8122         if(!a){
8123             cb();
8124         }
8125     },
8126
8127     // overridden Element method
8128     setBounds : function(x, y, w, h, a, d, c, e){
8129         this.beforeAction();
8130         var cb = this.createCB(c);
8131         if(!a){
8132             this.storeXY([x, y]);
8133             supr.setXY.call(this, [x, y]);
8134             supr.setSize.call(this, w, h, a, d, cb, e);
8135             cb();
8136         }else{
8137             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8138         }
8139         return this;
8140     },
8141     
8142     /**
8143      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8144      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8145      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8146      * @param {Number} zindex The new z-index to set
8147      * @return {this} The Layer
8148      */
8149     setZIndex : function(zindex){
8150         this.zindex = zindex;
8151         this.setStyle("z-index", zindex + 2);
8152         if(this.shadow){
8153             this.shadow.setZIndex(zindex + 1);
8154         }
8155         if(this.shim){
8156             this.shim.setStyle("z-index", zindex);
8157         }
8158     }
8159 });
8160 })();/*
8161  * Based on:
8162  * Ext JS Library 1.1.1
8163  * Copyright(c) 2006-2007, Ext JS, LLC.
8164  *
8165  * Originally Released Under LGPL - original licence link has changed is not relivant.
8166  *
8167  * Fork - LGPL
8168  * <script type="text/javascript">
8169  */
8170
8171
8172 /**
8173  * @class Roo.Shadow
8174  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8175  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8176  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8177  * @constructor
8178  * Create a new Shadow
8179  * @param {Object} config The config object
8180  */
8181 Roo.Shadow = function(config){
8182     Roo.apply(this, config);
8183     if(typeof this.mode != "string"){
8184         this.mode = this.defaultMode;
8185     }
8186     var o = this.offset, a = {h: 0};
8187     var rad = Math.floor(this.offset/2);
8188     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8189         case "drop":
8190             a.w = 0;
8191             a.l = a.t = o;
8192             a.t -= 1;
8193             if(Roo.isIE){
8194                 a.l -= this.offset + rad;
8195                 a.t -= this.offset + rad;
8196                 a.w -= rad;
8197                 a.h -= rad;
8198                 a.t += 1;
8199             }
8200         break;
8201         case "sides":
8202             a.w = (o*2);
8203             a.l = -o;
8204             a.t = o-1;
8205             if(Roo.isIE){
8206                 a.l -= (this.offset - rad);
8207                 a.t -= this.offset + rad;
8208                 a.l += 1;
8209                 a.w -= (this.offset - rad)*2;
8210                 a.w -= rad + 1;
8211                 a.h -= 1;
8212             }
8213         break;
8214         case "frame":
8215             a.w = a.h = (o*2);
8216             a.l = a.t = -o;
8217             a.t += 1;
8218             a.h -= 2;
8219             if(Roo.isIE){
8220                 a.l -= (this.offset - rad);
8221                 a.t -= (this.offset - rad);
8222                 a.l += 1;
8223                 a.w -= (this.offset + rad + 1);
8224                 a.h -= (this.offset + rad);
8225                 a.h += 1;
8226             }
8227         break;
8228     };
8229
8230     this.adjusts = a;
8231 };
8232
8233 Roo.Shadow.prototype = {
8234     /**
8235      * @cfg {String} mode
8236      * The shadow display mode.  Supports the following options:<br />
8237      * sides: Shadow displays on both sides and bottom only<br />
8238      * frame: Shadow displays equally on all four sides<br />
8239      * drop: Traditional bottom-right drop shadow (default)
8240      */
8241     /**
8242      * @cfg {String} offset
8243      * The number of pixels to offset the shadow from the element (defaults to 4)
8244      */
8245     offset: 4,
8246
8247     // private
8248     defaultMode: "drop",
8249
8250     /**
8251      * Displays the shadow under the target element
8252      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8253      */
8254     show : function(target){
8255         target = Roo.get(target);
8256         if(!this.el){
8257             this.el = Roo.Shadow.Pool.pull();
8258             if(this.el.dom.nextSibling != target.dom){
8259                 this.el.insertBefore(target);
8260             }
8261         }
8262         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8263         if(Roo.isIE){
8264             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8265         }
8266         this.realign(
8267             target.getLeft(true),
8268             target.getTop(true),
8269             target.getWidth(),
8270             target.getHeight()
8271         );
8272         this.el.dom.style.display = "block";
8273     },
8274
8275     /**
8276      * Returns true if the shadow is visible, else false
8277      */
8278     isVisible : function(){
8279         return this.el ? true : false;  
8280     },
8281
8282     /**
8283      * Direct alignment when values are already available. Show must be called at least once before
8284      * calling this method to ensure it is initialized.
8285      * @param {Number} left The target element left position
8286      * @param {Number} top The target element top position
8287      * @param {Number} width The target element width
8288      * @param {Number} height The target element height
8289      */
8290     realign : function(l, t, w, h){
8291         if(!this.el){
8292             return;
8293         }
8294         var a = this.adjusts, d = this.el.dom, s = d.style;
8295         var iea = 0;
8296         s.left = (l+a.l)+"px";
8297         s.top = (t+a.t)+"px";
8298         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8299  
8300         if(s.width != sws || s.height != shs){
8301             s.width = sws;
8302             s.height = shs;
8303             if(!Roo.isIE){
8304                 var cn = d.childNodes;
8305                 var sww = Math.max(0, (sw-12))+"px";
8306                 cn[0].childNodes[1].style.width = sww;
8307                 cn[1].childNodes[1].style.width = sww;
8308                 cn[2].childNodes[1].style.width = sww;
8309                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8310             }
8311         }
8312     },
8313
8314     /**
8315      * Hides this shadow
8316      */
8317     hide : function(){
8318         if(this.el){
8319             this.el.dom.style.display = "none";
8320             Roo.Shadow.Pool.push(this.el);
8321             delete this.el;
8322         }
8323     },
8324
8325     /**
8326      * Adjust the z-index of this shadow
8327      * @param {Number} zindex The new z-index
8328      */
8329     setZIndex : function(z){
8330         this.zIndex = z;
8331         if(this.el){
8332             this.el.setStyle("z-index", z);
8333         }
8334     }
8335 };
8336
8337 // Private utility class that manages the internal Shadow cache
8338 Roo.Shadow.Pool = function(){
8339     var p = [];
8340     var markup = Roo.isIE ?
8341                  '<div class="x-ie-shadow"></div>' :
8342                  '<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>';
8343     return {
8344         pull : function(){
8345             var sh = p.shift();
8346             if(!sh){
8347                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8348                 sh.autoBoxAdjust = false;
8349             }
8350             return sh;
8351         },
8352
8353         push : function(sh){
8354             p.push(sh);
8355         }
8356     };
8357 }();/*
8358  * Based on:
8359  * Ext JS Library 1.1.1
8360  * Copyright(c) 2006-2007, Ext JS, LLC.
8361  *
8362  * Originally Released Under LGPL - original licence link has changed is not relivant.
8363  *
8364  * Fork - LGPL
8365  * <script type="text/javascript">
8366  */
8367
8368 /**
8369  * @class Roo.BoxComponent
8370  * @extends Roo.Component
8371  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8372  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8373  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8374  * layout containers.
8375  * @constructor
8376  * @param {Roo.Element/String/Object} config The configuration options.
8377  */
8378 Roo.BoxComponent = function(config){
8379     Roo.Component.call(this, config);
8380     this.addEvents({
8381         /**
8382          * @event resize
8383          * Fires after the component is resized.
8384              * @param {Roo.Component} this
8385              * @param {Number} adjWidth The box-adjusted width that was set
8386              * @param {Number} adjHeight The box-adjusted height that was set
8387              * @param {Number} rawWidth The width that was originally specified
8388              * @param {Number} rawHeight The height that was originally specified
8389              */
8390         resize : true,
8391         /**
8392          * @event move
8393          * Fires after the component is moved.
8394              * @param {Roo.Component} this
8395              * @param {Number} x The new x position
8396              * @param {Number} y The new y position
8397              */
8398         move : true
8399     });
8400 };
8401
8402 Roo.extend(Roo.BoxComponent, Roo.Component, {
8403     // private, set in afterRender to signify that the component has been rendered
8404     boxReady : false,
8405     // private, used to defer height settings to subclasses
8406     deferHeight: false,
8407     /** @cfg {Number} width
8408      * width (optional) size of component
8409      */
8410      /** @cfg {Number} height
8411      * height (optional) size of component
8412      */
8413      
8414     /**
8415      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8416      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8417      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8418      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8419      * @return {Roo.BoxComponent} this
8420      */
8421     setSize : function(w, h){
8422         // support for standard size objects
8423         if(typeof w == 'object'){
8424             h = w.height;
8425             w = w.width;
8426         }
8427         // not rendered
8428         if(!this.boxReady){
8429             this.width = w;
8430             this.height = h;
8431             return this;
8432         }
8433
8434         // prevent recalcs when not needed
8435         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8436             return this;
8437         }
8438         this.lastSize = {width: w, height: h};
8439
8440         var adj = this.adjustSize(w, h);
8441         var aw = adj.width, ah = adj.height;
8442         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8443             var rz = this.getResizeEl();
8444             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8445                 rz.setSize(aw, ah);
8446             }else if(!this.deferHeight && ah !== undefined){
8447                 rz.setHeight(ah);
8448             }else if(aw !== undefined){
8449                 rz.setWidth(aw);
8450             }
8451             this.onResize(aw, ah, w, h);
8452             this.fireEvent('resize', this, aw, ah, w, h);
8453         }
8454         return this;
8455     },
8456
8457     /**
8458      * Gets the current size of the component's underlying element.
8459      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8460      */
8461     getSize : function(){
8462         return this.el.getSize();
8463     },
8464
8465     /**
8466      * Gets the current XY position of the component's underlying element.
8467      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8468      * @return {Array} The XY position of the element (e.g., [100, 200])
8469      */
8470     getPosition : function(local){
8471         if(local === true){
8472             return [this.el.getLeft(true), this.el.getTop(true)];
8473         }
8474         return this.xy || this.el.getXY();
8475     },
8476
8477     /**
8478      * Gets the current box measurements of the component's underlying element.
8479      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8480      * @returns {Object} box An object in the format {x, y, width, height}
8481      */
8482     getBox : function(local){
8483         var s = this.el.getSize();
8484         if(local){
8485             s.x = this.el.getLeft(true);
8486             s.y = this.el.getTop(true);
8487         }else{
8488             var xy = this.xy || this.el.getXY();
8489             s.x = xy[0];
8490             s.y = xy[1];
8491         }
8492         return s;
8493     },
8494
8495     /**
8496      * Sets the current box measurements of the component's underlying element.
8497      * @param {Object} box An object in the format {x, y, width, height}
8498      * @returns {Roo.BoxComponent} this
8499      */
8500     updateBox : function(box){
8501         this.setSize(box.width, box.height);
8502         this.setPagePosition(box.x, box.y);
8503         return this;
8504     },
8505
8506     // protected
8507     getResizeEl : function(){
8508         return this.resizeEl || this.el;
8509     },
8510
8511     // protected
8512     getPositionEl : function(){
8513         return this.positionEl || this.el;
8514     },
8515
8516     /**
8517      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8518      * This method fires the move event.
8519      * @param {Number} left The new left
8520      * @param {Number} top The new top
8521      * @returns {Roo.BoxComponent} this
8522      */
8523     setPosition : function(x, y){
8524         this.x = x;
8525         this.y = y;
8526         if(!this.boxReady){
8527             return this;
8528         }
8529         var adj = this.adjustPosition(x, y);
8530         var ax = adj.x, ay = adj.y;
8531
8532         var el = this.getPositionEl();
8533         if(ax !== undefined || ay !== undefined){
8534             if(ax !== undefined && ay !== undefined){
8535                 el.setLeftTop(ax, ay);
8536             }else if(ax !== undefined){
8537                 el.setLeft(ax);
8538             }else if(ay !== undefined){
8539                 el.setTop(ay);
8540             }
8541             this.onPosition(ax, ay);
8542             this.fireEvent('move', this, ax, ay);
8543         }
8544         return this;
8545     },
8546
8547     /**
8548      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8549      * This method fires the move event.
8550      * @param {Number} x The new x position
8551      * @param {Number} y The new y position
8552      * @returns {Roo.BoxComponent} this
8553      */
8554     setPagePosition : function(x, y){
8555         this.pageX = x;
8556         this.pageY = y;
8557         if(!this.boxReady){
8558             return;
8559         }
8560         if(x === undefined || y === undefined){ // cannot translate undefined points
8561             return;
8562         }
8563         var p = this.el.translatePoints(x, y);
8564         this.setPosition(p.left, p.top);
8565         return this;
8566     },
8567
8568     // private
8569     onRender : function(ct, position){
8570         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8571         if(this.resizeEl){
8572             this.resizeEl = Roo.get(this.resizeEl);
8573         }
8574         if(this.positionEl){
8575             this.positionEl = Roo.get(this.positionEl);
8576         }
8577     },
8578
8579     // private
8580     afterRender : function(){
8581         Roo.BoxComponent.superclass.afterRender.call(this);
8582         this.boxReady = true;
8583         this.setSize(this.width, this.height);
8584         if(this.x || this.y){
8585             this.setPosition(this.x, this.y);
8586         }
8587         if(this.pageX || this.pageY){
8588             this.setPagePosition(this.pageX, this.pageY);
8589         }
8590     },
8591
8592     /**
8593      * Force the component's size to recalculate based on the underlying element's current height and width.
8594      * @returns {Roo.BoxComponent} this
8595      */
8596     syncSize : function(){
8597         delete this.lastSize;
8598         this.setSize(this.el.getWidth(), this.el.getHeight());
8599         return this;
8600     },
8601
8602     /**
8603      * Called after the component is resized, this method is empty by default but can be implemented by any
8604      * subclass that needs to perform custom logic after a resize occurs.
8605      * @param {Number} adjWidth The box-adjusted width that was set
8606      * @param {Number} adjHeight The box-adjusted height that was set
8607      * @param {Number} rawWidth The width that was originally specified
8608      * @param {Number} rawHeight The height that was originally specified
8609      */
8610     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8611
8612     },
8613
8614     /**
8615      * Called after the component is moved, this method is empty by default but can be implemented by any
8616      * subclass that needs to perform custom logic after a move occurs.
8617      * @param {Number} x The new x position
8618      * @param {Number} y The new y position
8619      */
8620     onPosition : function(x, y){
8621
8622     },
8623
8624     // private
8625     adjustSize : function(w, h){
8626         if(this.autoWidth){
8627             w = 'auto';
8628         }
8629         if(this.autoHeight){
8630             h = 'auto';
8631         }
8632         return {width : w, height: h};
8633     },
8634
8635     // private
8636     adjustPosition : function(x, y){
8637         return {x : x, y: y};
8638     }
8639 });/*
8640  * Based on:
8641  * Ext JS Library 1.1.1
8642  * Copyright(c) 2006-2007, Ext JS, LLC.
8643  *
8644  * Originally Released Under LGPL - original licence link has changed is not relivant.
8645  *
8646  * Fork - LGPL
8647  * <script type="text/javascript">
8648  */
8649
8650
8651 /**
8652  * @class Roo.SplitBar
8653  * @extends Roo.util.Observable
8654  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8655  * <br><br>
8656  * Usage:
8657  * <pre><code>
8658 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8659                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8660 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8661 split.minSize = 100;
8662 split.maxSize = 600;
8663 split.animate = true;
8664 split.on('moved', splitterMoved);
8665 </code></pre>
8666  * @constructor
8667  * Create a new SplitBar
8668  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8669  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8670  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8671  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8672                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8673                         position of the SplitBar).
8674  */
8675 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8676     
8677     /** @private */
8678     this.el = Roo.get(dragElement, true);
8679     this.el.dom.unselectable = "on";
8680     /** @private */
8681     this.resizingEl = Roo.get(resizingElement, true);
8682
8683     /**
8684      * @private
8685      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8686      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8687      * @type Number
8688      */
8689     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8690     
8691     /**
8692      * The minimum size of the resizing element. (Defaults to 0)
8693      * @type Number
8694      */
8695     this.minSize = 0;
8696     
8697     /**
8698      * The maximum size of the resizing element. (Defaults to 2000)
8699      * @type Number
8700      */
8701     this.maxSize = 2000;
8702     
8703     /**
8704      * Whether to animate the transition to the new size
8705      * @type Boolean
8706      */
8707     this.animate = false;
8708     
8709     /**
8710      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8711      * @type Boolean
8712      */
8713     this.useShim = false;
8714     
8715     /** @private */
8716     this.shim = null;
8717     
8718     if(!existingProxy){
8719         /** @private */
8720         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8721     }else{
8722         this.proxy = Roo.get(existingProxy).dom;
8723     }
8724     /** @private */
8725     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8726     
8727     /** @private */
8728     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8729     
8730     /** @private */
8731     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8732     
8733     /** @private */
8734     this.dragSpecs = {};
8735     
8736     /**
8737      * @private The adapter to use to positon and resize elements
8738      */
8739     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8740     this.adapter.init(this);
8741     
8742     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8743         /** @private */
8744         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8745         this.el.addClass("x-splitbar-h");
8746     }else{
8747         /** @private */
8748         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8749         this.el.addClass("x-splitbar-v");
8750     }
8751     
8752     this.addEvents({
8753         /**
8754          * @event resize
8755          * Fires when the splitter is moved (alias for {@link #event-moved})
8756          * @param {Roo.SplitBar} this
8757          * @param {Number} newSize the new width or height
8758          */
8759         "resize" : true,
8760         /**
8761          * @event moved
8762          * Fires when the splitter is moved
8763          * @param {Roo.SplitBar} this
8764          * @param {Number} newSize the new width or height
8765          */
8766         "moved" : true,
8767         /**
8768          * @event beforeresize
8769          * Fires before the splitter is dragged
8770          * @param {Roo.SplitBar} this
8771          */
8772         "beforeresize" : true,
8773
8774         "beforeapply" : true
8775     });
8776
8777     Roo.util.Observable.call(this);
8778 };
8779
8780 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8781     onStartProxyDrag : function(x, y){
8782         this.fireEvent("beforeresize", this);
8783         if(!this.overlay){
8784             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8785             o.unselectable();
8786             o.enableDisplayMode("block");
8787             // all splitbars share the same overlay
8788             Roo.SplitBar.prototype.overlay = o;
8789         }
8790         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8791         this.overlay.show();
8792         Roo.get(this.proxy).setDisplayed("block");
8793         var size = this.adapter.getElementSize(this);
8794         this.activeMinSize = this.getMinimumSize();;
8795         this.activeMaxSize = this.getMaximumSize();;
8796         var c1 = size - this.activeMinSize;
8797         var c2 = Math.max(this.activeMaxSize - size, 0);
8798         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8799             this.dd.resetConstraints();
8800             this.dd.setXConstraint(
8801                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8802                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8803             );
8804             this.dd.setYConstraint(0, 0);
8805         }else{
8806             this.dd.resetConstraints();
8807             this.dd.setXConstraint(0, 0);
8808             this.dd.setYConstraint(
8809                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8810                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8811             );
8812          }
8813         this.dragSpecs.startSize = size;
8814         this.dragSpecs.startPoint = [x, y];
8815         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8816     },
8817     
8818     /** 
8819      * @private Called after the drag operation by the DDProxy
8820      */
8821     onEndProxyDrag : function(e){
8822         Roo.get(this.proxy).setDisplayed(false);
8823         var endPoint = Roo.lib.Event.getXY(e);
8824         if(this.overlay){
8825             this.overlay.hide();
8826         }
8827         var newSize;
8828         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8829             newSize = this.dragSpecs.startSize + 
8830                 (this.placement == Roo.SplitBar.LEFT ?
8831                     endPoint[0] - this.dragSpecs.startPoint[0] :
8832                     this.dragSpecs.startPoint[0] - endPoint[0]
8833                 );
8834         }else{
8835             newSize = this.dragSpecs.startSize + 
8836                 (this.placement == Roo.SplitBar.TOP ?
8837                     endPoint[1] - this.dragSpecs.startPoint[1] :
8838                     this.dragSpecs.startPoint[1] - endPoint[1]
8839                 );
8840         }
8841         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8842         if(newSize != this.dragSpecs.startSize){
8843             if(this.fireEvent('beforeapply', this, newSize) !== false){
8844                 this.adapter.setElementSize(this, newSize);
8845                 this.fireEvent("moved", this, newSize);
8846                 this.fireEvent("resize", this, newSize);
8847             }
8848         }
8849     },
8850     
8851     /**
8852      * Get the adapter this SplitBar uses
8853      * @return The adapter object
8854      */
8855     getAdapter : function(){
8856         return this.adapter;
8857     },
8858     
8859     /**
8860      * Set the adapter this SplitBar uses
8861      * @param {Object} adapter A SplitBar adapter object
8862      */
8863     setAdapter : function(adapter){
8864         this.adapter = adapter;
8865         this.adapter.init(this);
8866     },
8867     
8868     /**
8869      * Gets the minimum size for the resizing element
8870      * @return {Number} The minimum size
8871      */
8872     getMinimumSize : function(){
8873         return this.minSize;
8874     },
8875     
8876     /**
8877      * Sets the minimum size for the resizing element
8878      * @param {Number} minSize The minimum size
8879      */
8880     setMinimumSize : function(minSize){
8881         this.minSize = minSize;
8882     },
8883     
8884     /**
8885      * Gets the maximum size for the resizing element
8886      * @return {Number} The maximum size
8887      */
8888     getMaximumSize : function(){
8889         return this.maxSize;
8890     },
8891     
8892     /**
8893      * Sets the maximum size for the resizing element
8894      * @param {Number} maxSize The maximum size
8895      */
8896     setMaximumSize : function(maxSize){
8897         this.maxSize = maxSize;
8898     },
8899     
8900     /**
8901      * Sets the initialize size for the resizing element
8902      * @param {Number} size The initial size
8903      */
8904     setCurrentSize : function(size){
8905         var oldAnimate = this.animate;
8906         this.animate = false;
8907         this.adapter.setElementSize(this, size);
8908         this.animate = oldAnimate;
8909     },
8910     
8911     /**
8912      * Destroy this splitbar. 
8913      * @param {Boolean} removeEl True to remove the element
8914      */
8915     destroy : function(removeEl){
8916         if(this.shim){
8917             this.shim.remove();
8918         }
8919         this.dd.unreg();
8920         this.proxy.parentNode.removeChild(this.proxy);
8921         if(removeEl){
8922             this.el.remove();
8923         }
8924     }
8925 });
8926
8927 /**
8928  * @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.
8929  */
8930 Roo.SplitBar.createProxy = function(dir){
8931     var proxy = new Roo.Element(document.createElement("div"));
8932     proxy.unselectable();
8933     var cls = 'x-splitbar-proxy';
8934     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8935     document.body.appendChild(proxy.dom);
8936     return proxy.dom;
8937 };
8938
8939 /** 
8940  * @class Roo.SplitBar.BasicLayoutAdapter
8941  * Default Adapter. It assumes the splitter and resizing element are not positioned
8942  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8943  */
8944 Roo.SplitBar.BasicLayoutAdapter = function(){
8945 };
8946
8947 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8948     // do nothing for now
8949     init : function(s){
8950     
8951     },
8952     /**
8953      * Called before drag operations to get the current size of the resizing element. 
8954      * @param {Roo.SplitBar} s The SplitBar using this adapter
8955      */
8956      getElementSize : function(s){
8957         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8958             return s.resizingEl.getWidth();
8959         }else{
8960             return s.resizingEl.getHeight();
8961         }
8962     },
8963     
8964     /**
8965      * Called after drag operations to set the size of the resizing element.
8966      * @param {Roo.SplitBar} s The SplitBar using this adapter
8967      * @param {Number} newSize The new size to set
8968      * @param {Function} onComplete A function to be invoked when resizing is complete
8969      */
8970     setElementSize : function(s, newSize, onComplete){
8971         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8972             if(!s.animate){
8973                 s.resizingEl.setWidth(newSize);
8974                 if(onComplete){
8975                     onComplete(s, newSize);
8976                 }
8977             }else{
8978                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8979             }
8980         }else{
8981             
8982             if(!s.animate){
8983                 s.resizingEl.setHeight(newSize);
8984                 if(onComplete){
8985                     onComplete(s, newSize);
8986                 }
8987             }else{
8988                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
8989             }
8990         }
8991     }
8992 };
8993
8994 /** 
8995  *@class Roo.SplitBar.AbsoluteLayoutAdapter
8996  * @extends Roo.SplitBar.BasicLayoutAdapter
8997  * Adapter that  moves the splitter element to align with the resized sizing element. 
8998  * Used with an absolute positioned SplitBar.
8999  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
9000  * document.body, make sure you assign an id to the body element.
9001  */
9002 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
9003     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
9004     this.container = Roo.get(container);
9005 };
9006
9007 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
9008     init : function(s){
9009         this.basic.init(s);
9010     },
9011     
9012     getElementSize : function(s){
9013         return this.basic.getElementSize(s);
9014     },
9015     
9016     setElementSize : function(s, newSize, onComplete){
9017         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
9018     },
9019     
9020     moveSplitter : function(s){
9021         var yes = Roo.SplitBar;
9022         switch(s.placement){
9023             case yes.LEFT:
9024                 s.el.setX(s.resizingEl.getRight());
9025                 break;
9026             case yes.RIGHT:
9027                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
9028                 break;
9029             case yes.TOP:
9030                 s.el.setY(s.resizingEl.getBottom());
9031                 break;
9032             case yes.BOTTOM:
9033                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
9034                 break;
9035         }
9036     }
9037 };
9038
9039 /**
9040  * Orientation constant - Create a vertical SplitBar
9041  * @static
9042  * @type Number
9043  */
9044 Roo.SplitBar.VERTICAL = 1;
9045
9046 /**
9047  * Orientation constant - Create a horizontal SplitBar
9048  * @static
9049  * @type Number
9050  */
9051 Roo.SplitBar.HORIZONTAL = 2;
9052
9053 /**
9054  * Placement constant - The resizing element is to the left of the splitter element
9055  * @static
9056  * @type Number
9057  */
9058 Roo.SplitBar.LEFT = 1;
9059
9060 /**
9061  * Placement constant - The resizing element is to the right of the splitter element
9062  * @static
9063  * @type Number
9064  */
9065 Roo.SplitBar.RIGHT = 2;
9066
9067 /**
9068  * Placement constant - The resizing element is positioned above the splitter element
9069  * @static
9070  * @type Number
9071  */
9072 Roo.SplitBar.TOP = 3;
9073
9074 /**
9075  * Placement constant - The resizing element is positioned under splitter element
9076  * @static
9077  * @type Number
9078  */
9079 Roo.SplitBar.BOTTOM = 4;
9080 /*
9081  * Based on:
9082  * Ext JS Library 1.1.1
9083  * Copyright(c) 2006-2007, Ext JS, LLC.
9084  *
9085  * Originally Released Under LGPL - original licence link has changed is not relivant.
9086  *
9087  * Fork - LGPL
9088  * <script type="text/javascript">
9089  */
9090
9091 /**
9092  * @class Roo.View
9093  * @extends Roo.util.Observable
9094  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9095  * This class also supports single and multi selection modes. <br>
9096  * Create a data model bound view:
9097  <pre><code>
9098  var store = new Roo.data.Store(...);
9099
9100  var view = new Roo.View({
9101     el : "my-element",
9102     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9103  
9104     singleSelect: true,
9105     selectedClass: "ydataview-selected",
9106     store: store
9107  });
9108
9109  // listen for node click?
9110  view.on("click", function(vw, index, node, e){
9111  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9112  });
9113
9114  // load XML data
9115  dataModel.load("foobar.xml");
9116  </code></pre>
9117  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9118  * <br><br>
9119  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9120  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9121  * 
9122  * Note: old style constructor is still suported (container, template, config)
9123  * 
9124  * @constructor
9125  * Create a new View
9126  * @param {Object} config The config object
9127  * 
9128  */
9129 Roo.View = function(config, depreciated_tpl, depreciated_config){
9130     
9131     if (typeof(depreciated_tpl) == 'undefined') {
9132         // new way.. - universal constructor.
9133         Roo.apply(this, config);
9134         this.el  = Roo.get(this.el);
9135     } else {
9136         // old format..
9137         this.el  = Roo.get(config);
9138         this.tpl = depreciated_tpl;
9139         Roo.apply(this, depreciated_config);
9140     }
9141      
9142     
9143     if(typeof(this.tpl) == "string"){
9144         this.tpl = new Roo.Template(this.tpl);
9145     } else {
9146         // support xtype ctors..
9147         this.tpl = new Roo.factory(this.tpl, Roo);
9148     }
9149     
9150     
9151     this.tpl.compile();
9152    
9153
9154      
9155     /** @private */
9156     this.addEvents({
9157         /**
9158          * @event beforeclick
9159          * Fires before a click is processed. Returns false to cancel the default action.
9160          * @param {Roo.View} this
9161          * @param {Number} index The index of the target node
9162          * @param {HTMLElement} node The target node
9163          * @param {Roo.EventObject} e The raw event object
9164          */
9165             "beforeclick" : true,
9166         /**
9167          * @event click
9168          * Fires when a template node is clicked.
9169          * @param {Roo.View} this
9170          * @param {Number} index The index of the target node
9171          * @param {HTMLElement} node The target node
9172          * @param {Roo.EventObject} e The raw event object
9173          */
9174             "click" : true,
9175         /**
9176          * @event dblclick
9177          * Fires when a template node is double clicked.
9178          * @param {Roo.View} this
9179          * @param {Number} index The index of the target node
9180          * @param {HTMLElement} node The target node
9181          * @param {Roo.EventObject} e The raw event object
9182          */
9183             "dblclick" : true,
9184         /**
9185          * @event contextmenu
9186          * Fires when a template node is right clicked.
9187          * @param {Roo.View} this
9188          * @param {Number} index The index of the target node
9189          * @param {HTMLElement} node The target node
9190          * @param {Roo.EventObject} e The raw event object
9191          */
9192             "contextmenu" : true,
9193         /**
9194          * @event selectionchange
9195          * Fires when the selected nodes change.
9196          * @param {Roo.View} this
9197          * @param {Array} selections Array of the selected nodes
9198          */
9199             "selectionchange" : true,
9200     
9201         /**
9202          * @event beforeselect
9203          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9204          * @param {Roo.View} this
9205          * @param {HTMLElement} node The node to be selected
9206          * @param {Array} selections Array of currently selected nodes
9207          */
9208             "beforeselect" : true,
9209         /**
9210          * @event preparedata
9211          * Fires on every row to render, to allow you to change the data.
9212          * @param {Roo.View} this
9213          * @param {Object} data to be rendered (change this)
9214          */
9215           "preparedata" : true
9216         });
9217
9218     this.el.on({
9219         "click": this.onClick,
9220         "dblclick": this.onDblClick,
9221         "contextmenu": this.onContextMenu,
9222         scope:this
9223     });
9224
9225     this.selections = [];
9226     this.nodes = [];
9227     this.cmp = new Roo.CompositeElementLite([]);
9228     if(this.store){
9229         this.store = Roo.factory(this.store, Roo.data);
9230         this.setStore(this.store, true);
9231     }
9232     Roo.View.superclass.constructor.call(this);
9233 };
9234
9235 Roo.extend(Roo.View, Roo.util.Observable, {
9236     
9237      /**
9238      * @cfg {Roo.data.Store} store Data store to load data from.
9239      */
9240     store : false,
9241     
9242     /**
9243      * @cfg {String|Roo.Element} el The container element.
9244      */
9245     el : '',
9246     
9247     /**
9248      * @cfg {String|Roo.Template} tpl The template used by this View 
9249      */
9250     tpl : false,
9251     
9252     /**
9253      * @cfg {String} selectedClass The css class to add to selected nodes
9254      */
9255     selectedClass : "x-view-selected",
9256      /**
9257      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9258      */
9259     emptyText : "",
9260     /**
9261      * @cfg {Boolean} multiSelect Allow multiple selection
9262      */
9263     
9264     multiSelect : false,
9265     /**
9266      * @cfg {Boolean} singleSelect Allow single selection
9267      */
9268     singleSelect:  false,
9269     
9270     /**
9271      * Returns the element this view is bound to.
9272      * @return {Roo.Element}
9273      */
9274     getEl : function(){
9275         return this.el;
9276     },
9277
9278     /**
9279      * Refreshes the view.
9280      */
9281     refresh : function(){
9282         var t = this.tpl;
9283         this.clearSelections();
9284         this.el.update("");
9285         var html = [];
9286         var records = this.store.getRange();
9287         if(records.length < 1){
9288             this.el.update(this.emptyText);
9289             return;
9290         }
9291         for(var i = 0, len = records.length; i < len; i++){
9292             var data = this.prepareData(records[i].data, i, records[i]);
9293             this.fireEvent("preparedata", this, data, i, records[i]);
9294             html[html.length] = t.apply(data);
9295         }
9296         this.el.update(html.join(""));
9297         this.nodes = this.el.dom.childNodes;
9298         this.updateIndexes(0);
9299     },
9300
9301     /**
9302      * Function to override to reformat the data that is sent to
9303      * the template for each node.
9304      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9305      * a JSON object for an UpdateManager bound view).
9306      */
9307     prepareData : function(data){
9308         return data;
9309     },
9310
9311     onUpdate : function(ds, record){
9312         this.clearSelections();
9313         var index = this.store.indexOf(record);
9314         var n = this.nodes[index];
9315         this.tpl.insertBefore(n, this.prepareData(record.data));
9316         n.parentNode.removeChild(n);
9317         this.updateIndexes(index, index);
9318     },
9319
9320     onAdd : function(ds, records, index){
9321         this.clearSelections();
9322         if(this.nodes.length == 0){
9323             this.refresh();
9324             return;
9325         }
9326         var n = this.nodes[index];
9327         for(var i = 0, len = records.length; i < len; i++){
9328             var d = this.prepareData(records[i].data);
9329             if(n){
9330                 this.tpl.insertBefore(n, d);
9331             }else{
9332                 this.tpl.append(this.el, d);
9333             }
9334         }
9335         this.updateIndexes(index);
9336     },
9337
9338     onRemove : function(ds, record, index){
9339         this.clearSelections();
9340         this.el.dom.removeChild(this.nodes[index]);
9341         this.updateIndexes(index);
9342     },
9343
9344     /**
9345      * Refresh an individual node.
9346      * @param {Number} index
9347      */
9348     refreshNode : function(index){
9349         this.onUpdate(this.store, this.store.getAt(index));
9350     },
9351
9352     updateIndexes : function(startIndex, endIndex){
9353         var ns = this.nodes;
9354         startIndex = startIndex || 0;
9355         endIndex = endIndex || ns.length - 1;
9356         for(var i = startIndex; i <= endIndex; i++){
9357             ns[i].nodeIndex = i;
9358         }
9359     },
9360
9361     /**
9362      * Changes the data store this view uses and refresh the view.
9363      * @param {Store} store
9364      */
9365     setStore : function(store, initial){
9366         if(!initial && this.store){
9367             this.store.un("datachanged", this.refresh);
9368             this.store.un("add", this.onAdd);
9369             this.store.un("remove", this.onRemove);
9370             this.store.un("update", this.onUpdate);
9371             this.store.un("clear", this.refresh);
9372         }
9373         if(store){
9374           
9375             store.on("datachanged", this.refresh, this);
9376             store.on("add", this.onAdd, this);
9377             store.on("remove", this.onRemove, this);
9378             store.on("update", this.onUpdate, this);
9379             store.on("clear", this.refresh, this);
9380         }
9381         
9382         if(store){
9383             this.refresh();
9384         }
9385     },
9386
9387     /**
9388      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9389      * @param {HTMLElement} node
9390      * @return {HTMLElement} The template node
9391      */
9392     findItemFromChild : function(node){
9393         var el = this.el.dom;
9394         if(!node || node.parentNode == el){
9395                     return node;
9396             }
9397             var p = node.parentNode;
9398             while(p && p != el){
9399             if(p.parentNode == el){
9400                 return p;
9401             }
9402             p = p.parentNode;
9403         }
9404             return null;
9405     },
9406
9407     /** @ignore */
9408     onClick : function(e){
9409         var item = this.findItemFromChild(e.getTarget());
9410         if(item){
9411             var index = this.indexOf(item);
9412             if(this.onItemClick(item, index, e) !== false){
9413                 this.fireEvent("click", this, index, item, e);
9414             }
9415         }else{
9416             this.clearSelections();
9417         }
9418     },
9419
9420     /** @ignore */
9421     onContextMenu : function(e){
9422         var item = this.findItemFromChild(e.getTarget());
9423         if(item){
9424             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9425         }
9426     },
9427
9428     /** @ignore */
9429     onDblClick : function(e){
9430         var item = this.findItemFromChild(e.getTarget());
9431         if(item){
9432             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9433         }
9434     },
9435
9436     onItemClick : function(item, index, e){
9437         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9438             return false;
9439         }
9440         if(this.multiSelect || this.singleSelect){
9441             if(this.multiSelect && e.shiftKey && this.lastSelection){
9442                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9443             }else{
9444                 this.select(item, this.multiSelect && e.ctrlKey);
9445                 this.lastSelection = item;
9446             }
9447             e.preventDefault();
9448         }
9449         return true;
9450     },
9451
9452     /**
9453      * Get the number of selected nodes.
9454      * @return {Number}
9455      */
9456     getSelectionCount : function(){
9457         return this.selections.length;
9458     },
9459
9460     /**
9461      * Get the currently selected nodes.
9462      * @return {Array} An array of HTMLElements
9463      */
9464     getSelectedNodes : function(){
9465         return this.selections;
9466     },
9467
9468     /**
9469      * Get the indexes of the selected nodes.
9470      * @return {Array}
9471      */
9472     getSelectedIndexes : function(){
9473         var indexes = [], s = this.selections;
9474         for(var i = 0, len = s.length; i < len; i++){
9475             indexes.push(s[i].nodeIndex);
9476         }
9477         return indexes;
9478     },
9479
9480     /**
9481      * Clear all selections
9482      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9483      */
9484     clearSelections : function(suppressEvent){
9485         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9486             this.cmp.elements = this.selections;
9487             this.cmp.removeClass(this.selectedClass);
9488             this.selections = [];
9489             if(!suppressEvent){
9490                 this.fireEvent("selectionchange", this, this.selections);
9491             }
9492         }
9493     },
9494
9495     /**
9496      * Returns true if the passed node is selected
9497      * @param {HTMLElement/Number} node The node or node index
9498      * @return {Boolean}
9499      */
9500     isSelected : function(node){
9501         var s = this.selections;
9502         if(s.length < 1){
9503             return false;
9504         }
9505         node = this.getNode(node);
9506         return s.indexOf(node) !== -1;
9507     },
9508
9509     /**
9510      * Selects nodes.
9511      * @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
9512      * @param {Boolean} keepExisting (optional) true to keep existing selections
9513      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9514      */
9515     select : function(nodeInfo, keepExisting, suppressEvent){
9516         if(nodeInfo instanceof Array){
9517             if(!keepExisting){
9518                 this.clearSelections(true);
9519             }
9520             for(var i = 0, len = nodeInfo.length; i < len; i++){
9521                 this.select(nodeInfo[i], true, true);
9522             }
9523         } else{
9524             var node = this.getNode(nodeInfo);
9525             if(node && !this.isSelected(node)){
9526                 if(!keepExisting){
9527                     this.clearSelections(true);
9528                 }
9529                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9530                     Roo.fly(node).addClass(this.selectedClass);
9531                     this.selections.push(node);
9532                     if(!suppressEvent){
9533                         this.fireEvent("selectionchange", this, this.selections);
9534                     }
9535                 }
9536             }
9537         }
9538     },
9539
9540     /**
9541      * Gets a template node.
9542      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9543      * @return {HTMLElement} The node or null if it wasn't found
9544      */
9545     getNode : function(nodeInfo){
9546         if(typeof nodeInfo == "string"){
9547             return document.getElementById(nodeInfo);
9548         }else if(typeof nodeInfo == "number"){
9549             return this.nodes[nodeInfo];
9550         }
9551         return nodeInfo;
9552     },
9553
9554     /**
9555      * Gets a range template nodes.
9556      * @param {Number} startIndex
9557      * @param {Number} endIndex
9558      * @return {Array} An array of nodes
9559      */
9560     getNodes : function(start, end){
9561         var ns = this.nodes;
9562         start = start || 0;
9563         end = typeof end == "undefined" ? ns.length - 1 : end;
9564         var nodes = [];
9565         if(start <= end){
9566             for(var i = start; i <= end; i++){
9567                 nodes.push(ns[i]);
9568             }
9569         } else{
9570             for(var i = start; i >= end; i--){
9571                 nodes.push(ns[i]);
9572             }
9573         }
9574         return nodes;
9575     },
9576
9577     /**
9578      * Finds the index of the passed node
9579      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9580      * @return {Number} The index of the node or -1
9581      */
9582     indexOf : function(node){
9583         node = this.getNode(node);
9584         if(typeof node.nodeIndex == "number"){
9585             return node.nodeIndex;
9586         }
9587         var ns = this.nodes;
9588         for(var i = 0, len = ns.length; i < len; i++){
9589             if(ns[i] == node){
9590                 return i;
9591             }
9592         }
9593         return -1;
9594     }
9595 });
9596 /*
9597  * Based on:
9598  * Ext JS Library 1.1.1
9599  * Copyright(c) 2006-2007, Ext JS, LLC.
9600  *
9601  * Originally Released Under LGPL - original licence link has changed is not relivant.
9602  *
9603  * Fork - LGPL
9604  * <script type="text/javascript">
9605  */
9606
9607 /**
9608  * @class Roo.JsonView
9609  * @extends Roo.View
9610  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9611 <pre><code>
9612 var view = new Roo.JsonView({
9613     container: "my-element",
9614     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9615     multiSelect: true, 
9616     jsonRoot: "data" 
9617 });
9618
9619 // listen for node click?
9620 view.on("click", function(vw, index, node, e){
9621     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9622 });
9623
9624 // direct load of JSON data
9625 view.load("foobar.php");
9626
9627 // Example from my blog list
9628 var tpl = new Roo.Template(
9629     '&lt;div class="entry"&gt;' +
9630     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9631     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9632     "&lt;/div&gt;&lt;hr /&gt;"
9633 );
9634
9635 var moreView = new Roo.JsonView({
9636     container :  "entry-list", 
9637     template : tpl,
9638     jsonRoot: "posts"
9639 });
9640 moreView.on("beforerender", this.sortEntries, this);
9641 moreView.load({
9642     url: "/blog/get-posts.php",
9643     params: "allposts=true",
9644     text: "Loading Blog Entries..."
9645 });
9646 </code></pre>
9647
9648 * Note: old code is supported with arguments : (container, template, config)
9649
9650
9651  * @constructor
9652  * Create a new JsonView
9653  * 
9654  * @param {Object} config The config object
9655  * 
9656  */
9657 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9658     
9659     
9660     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9661
9662     var um = this.el.getUpdateManager();
9663     um.setRenderer(this);
9664     um.on("update", this.onLoad, this);
9665     um.on("failure", this.onLoadException, this);
9666
9667     /**
9668      * @event beforerender
9669      * Fires before rendering of the downloaded JSON data.
9670      * @param {Roo.JsonView} this
9671      * @param {Object} data The JSON data loaded
9672      */
9673     /**
9674      * @event load
9675      * Fires when data is loaded.
9676      * @param {Roo.JsonView} this
9677      * @param {Object} data The JSON data loaded
9678      * @param {Object} response The raw Connect response object
9679      */
9680     /**
9681      * @event loadexception
9682      * Fires when loading fails.
9683      * @param {Roo.JsonView} this
9684      * @param {Object} response The raw Connect response object
9685      */
9686     this.addEvents({
9687         'beforerender' : true,
9688         'load' : true,
9689         'loadexception' : true
9690     });
9691 };
9692 Roo.extend(Roo.JsonView, Roo.View, {
9693     /**
9694      * @type {String} The root property in the loaded JSON object that contains the data
9695      */
9696     jsonRoot : "",
9697
9698     /**
9699      * Refreshes the view.
9700      */
9701     refresh : function(){
9702         this.clearSelections();
9703         this.el.update("");
9704         var html = [];
9705         var o = this.jsonData;
9706         if(o && o.length > 0){
9707             for(var i = 0, len = o.length; i < len; i++){
9708                 var data = this.prepareData(o[i], i, o);
9709                 html[html.length] = this.tpl.apply(data);
9710             }
9711         }else{
9712             html.push(this.emptyText);
9713         }
9714         this.el.update(html.join(""));
9715         this.nodes = this.el.dom.childNodes;
9716         this.updateIndexes(0);
9717     },
9718
9719     /**
9720      * 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.
9721      * @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:
9722      <pre><code>
9723      view.load({
9724          url: "your-url.php",
9725          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9726          callback: yourFunction,
9727          scope: yourObject, //(optional scope)
9728          discardUrl: false,
9729          nocache: false,
9730          text: "Loading...",
9731          timeout: 30,
9732          scripts: false
9733      });
9734      </code></pre>
9735      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9736      * 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.
9737      * @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}
9738      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9739      * @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.
9740      */
9741     load : function(){
9742         var um = this.el.getUpdateManager();
9743         um.update.apply(um, arguments);
9744     },
9745
9746     render : function(el, response){
9747         this.clearSelections();
9748         this.el.update("");
9749         var o;
9750         try{
9751             o = Roo.util.JSON.decode(response.responseText);
9752             if(this.jsonRoot){
9753                 
9754                 o = o[this.jsonRoot];
9755             }
9756         } catch(e){
9757         }
9758         /**
9759          * The current JSON data or null
9760          */
9761         this.jsonData = o;
9762         this.beforeRender();
9763         this.refresh();
9764     },
9765
9766 /**
9767  * Get the number of records in the current JSON dataset
9768  * @return {Number}
9769  */
9770     getCount : function(){
9771         return this.jsonData ? this.jsonData.length : 0;
9772     },
9773
9774 /**
9775  * Returns the JSON object for the specified node(s)
9776  * @param {HTMLElement/Array} node The node or an array of nodes
9777  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9778  * you get the JSON object for the node
9779  */
9780     getNodeData : function(node){
9781         if(node instanceof Array){
9782             var data = [];
9783             for(var i = 0, len = node.length; i < len; i++){
9784                 data.push(this.getNodeData(node[i]));
9785             }
9786             return data;
9787         }
9788         return this.jsonData[this.indexOf(node)] || null;
9789     },
9790
9791     beforeRender : function(){
9792         this.snapshot = this.jsonData;
9793         if(this.sortInfo){
9794             this.sort.apply(this, this.sortInfo);
9795         }
9796         this.fireEvent("beforerender", this, this.jsonData);
9797     },
9798
9799     onLoad : function(el, o){
9800         this.fireEvent("load", this, this.jsonData, o);
9801     },
9802
9803     onLoadException : function(el, o){
9804         this.fireEvent("loadexception", this, o);
9805     },
9806
9807 /**
9808  * Filter the data by a specific property.
9809  * @param {String} property A property on your JSON objects
9810  * @param {String/RegExp} value Either string that the property values
9811  * should start with, or a RegExp to test against the property
9812  */
9813     filter : function(property, value){
9814         if(this.jsonData){
9815             var data = [];
9816             var ss = this.snapshot;
9817             if(typeof value == "string"){
9818                 var vlen = value.length;
9819                 if(vlen == 0){
9820                     this.clearFilter();
9821                     return;
9822                 }
9823                 value = value.toLowerCase();
9824                 for(var i = 0, len = ss.length; i < len; i++){
9825                     var o = ss[i];
9826                     if(o[property].substr(0, vlen).toLowerCase() == value){
9827                         data.push(o);
9828                     }
9829                 }
9830             } else if(value.exec){ // regex?
9831                 for(var i = 0, len = ss.length; i < len; i++){
9832                     var o = ss[i];
9833                     if(value.test(o[property])){
9834                         data.push(o);
9835                     }
9836                 }
9837             } else{
9838                 return;
9839             }
9840             this.jsonData = data;
9841             this.refresh();
9842         }
9843     },
9844
9845 /**
9846  * Filter by a function. The passed function will be called with each
9847  * object in the current dataset. If the function returns true the value is kept,
9848  * otherwise it is filtered.
9849  * @param {Function} fn
9850  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9851  */
9852     filterBy : function(fn, scope){
9853         if(this.jsonData){
9854             var data = [];
9855             var ss = this.snapshot;
9856             for(var i = 0, len = ss.length; i < len; i++){
9857                 var o = ss[i];
9858                 if(fn.call(scope || this, o)){
9859                     data.push(o);
9860                 }
9861             }
9862             this.jsonData = data;
9863             this.refresh();
9864         }
9865     },
9866
9867 /**
9868  * Clears the current filter.
9869  */
9870     clearFilter : function(){
9871         if(this.snapshot && this.jsonData != this.snapshot){
9872             this.jsonData = this.snapshot;
9873             this.refresh();
9874         }
9875     },
9876
9877
9878 /**
9879  * Sorts the data for this view and refreshes it.
9880  * @param {String} property A property on your JSON objects to sort on
9881  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9882  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9883  */
9884     sort : function(property, dir, sortType){
9885         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9886         if(this.jsonData){
9887             var p = property;
9888             var dsc = dir && dir.toLowerCase() == "desc";
9889             var f = function(o1, o2){
9890                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9891                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9892                 ;
9893                 if(v1 < v2){
9894                     return dsc ? +1 : -1;
9895                 } else if(v1 > v2){
9896                     return dsc ? -1 : +1;
9897                 } else{
9898                     return 0;
9899                 }
9900             };
9901             this.jsonData.sort(f);
9902             this.refresh();
9903             if(this.jsonData != this.snapshot){
9904                 this.snapshot.sort(f);
9905             }
9906         }
9907     }
9908 });/*
9909  * Based on:
9910  * Ext JS Library 1.1.1
9911  * Copyright(c) 2006-2007, Ext JS, LLC.
9912  *
9913  * Originally Released Under LGPL - original licence link has changed is not relivant.
9914  *
9915  * Fork - LGPL
9916  * <script type="text/javascript">
9917  */
9918  
9919
9920 /**
9921  * @class Roo.ColorPalette
9922  * @extends Roo.Component
9923  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
9924  * Here's an example of typical usage:
9925  * <pre><code>
9926 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
9927 cp.render('my-div');
9928
9929 cp.on('select', function(palette, selColor){
9930     // do something with selColor
9931 });
9932 </code></pre>
9933  * @constructor
9934  * Create a new ColorPalette
9935  * @param {Object} config The config object
9936  */
9937 Roo.ColorPalette = function(config){
9938     Roo.ColorPalette.superclass.constructor.call(this, config);
9939     this.addEvents({
9940         /**
9941              * @event select
9942              * Fires when a color is selected
9943              * @param {ColorPalette} this
9944              * @param {String} color The 6-digit color hex code (without the # symbol)
9945              */
9946         select: true
9947     });
9948
9949     if(this.handler){
9950         this.on("select", this.handler, this.scope, true);
9951     }
9952 };
9953 Roo.extend(Roo.ColorPalette, Roo.Component, {
9954     /**
9955      * @cfg {String} itemCls
9956      * The CSS class to apply to the containing element (defaults to "x-color-palette")
9957      */
9958     itemCls : "x-color-palette",
9959     /**
9960      * @cfg {String} value
9961      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
9962      * the hex codes are case-sensitive.
9963      */
9964     value : null,
9965     clickEvent:'click',
9966     // private
9967     ctype: "Roo.ColorPalette",
9968
9969     /**
9970      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
9971      */
9972     allowReselect : false,
9973
9974     /**
9975      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
9976      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
9977      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
9978      * of colors with the width setting until the box is symmetrical.</p>
9979      * <p>You can override individual colors if needed:</p>
9980      * <pre><code>
9981 var cp = new Roo.ColorPalette();
9982 cp.colors[0] = "FF0000";  // change the first box to red
9983 </code></pre>
9984
9985 Or you can provide a custom array of your own for complete control:
9986 <pre><code>
9987 var cp = new Roo.ColorPalette();
9988 cp.colors = ["000000", "993300", "333300"];
9989 </code></pre>
9990      * @type Array
9991      */
9992     colors : [
9993         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
9994         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
9995         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
9996         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
9997         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
9998     ],
9999
10000     // private
10001     onRender : function(container, position){
10002         var t = new Roo.MasterTemplate(
10003             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
10004         );
10005         var c = this.colors;
10006         for(var i = 0, len = c.length; i < len; i++){
10007             t.add([c[i]]);
10008         }
10009         var el = document.createElement("div");
10010         el.className = this.itemCls;
10011         t.overwrite(el);
10012         container.dom.insertBefore(el, position);
10013         this.el = Roo.get(el);
10014         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
10015         if(this.clickEvent != 'click'){
10016             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
10017         }
10018     },
10019
10020     // private
10021     afterRender : function(){
10022         Roo.ColorPalette.superclass.afterRender.call(this);
10023         if(this.value){
10024             var s = this.value;
10025             this.value = null;
10026             this.select(s);
10027         }
10028     },
10029
10030     // private
10031     handleClick : function(e, t){
10032         e.preventDefault();
10033         if(!this.disabled){
10034             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
10035             this.select(c.toUpperCase());
10036         }
10037     },
10038
10039     /**
10040      * Selects the specified color in the palette (fires the select event)
10041      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
10042      */
10043     select : function(color){
10044         color = color.replace("#", "");
10045         if(color != this.value || this.allowReselect){
10046             var el = this.el;
10047             if(this.value){
10048                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
10049             }
10050             el.child("a.color-"+color).addClass("x-color-palette-sel");
10051             this.value = color;
10052             this.fireEvent("select", this, color);
10053         }
10054     }
10055 });/*
10056  * Based on:
10057  * Ext JS Library 1.1.1
10058  * Copyright(c) 2006-2007, Ext JS, LLC.
10059  *
10060  * Originally Released Under LGPL - original licence link has changed is not relivant.
10061  *
10062  * Fork - LGPL
10063  * <script type="text/javascript">
10064  */
10065  
10066 /**
10067  * @class Roo.DatePicker
10068  * @extends Roo.Component
10069  * Simple date picker class.
10070  * @constructor
10071  * Create a new DatePicker
10072  * @param {Object} config The config object
10073  */
10074 Roo.DatePicker = function(config){
10075     Roo.DatePicker.superclass.constructor.call(this, config);
10076
10077     this.value = config && config.value ?
10078                  config.value.clearTime() : new Date().clearTime();
10079
10080     this.addEvents({
10081         /**
10082              * @event select
10083              * Fires when a date is selected
10084              * @param {DatePicker} this
10085              * @param {Date} date The selected date
10086              */
10087         select: true
10088     });
10089
10090     if(this.handler){
10091         this.on("select", this.handler,  this.scope || this);
10092     }
10093     // build the disabledDatesRE
10094     if(!this.disabledDatesRE && this.disabledDates){
10095         var dd = this.disabledDates;
10096         var re = "(?:";
10097         for(var i = 0; i < dd.length; i++){
10098             re += dd[i];
10099             if(i != dd.length-1) re += "|";
10100         }
10101         this.disabledDatesRE = new RegExp(re + ")");
10102     }
10103 };
10104
10105 Roo.extend(Roo.DatePicker, Roo.Component, {
10106     /**
10107      * @cfg {String} todayText
10108      * The text to display on the button that selects the current date (defaults to "Today")
10109      */
10110     todayText : "Today",
10111     /**
10112      * @cfg {String} okText
10113      * The text to display on the ok button
10114      */
10115     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10116     /**
10117      * @cfg {String} cancelText
10118      * The text to display on the cancel button
10119      */
10120     cancelText : "Cancel",
10121     /**
10122      * @cfg {String} todayTip
10123      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10124      */
10125     todayTip : "{0} (Spacebar)",
10126     /**
10127      * @cfg {Date} minDate
10128      * Minimum allowable date (JavaScript date object, defaults to null)
10129      */
10130     minDate : null,
10131     /**
10132      * @cfg {Date} maxDate
10133      * Maximum allowable date (JavaScript date object, defaults to null)
10134      */
10135     maxDate : null,
10136     /**
10137      * @cfg {String} minText
10138      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10139      */
10140     minText : "This date is before the minimum date",
10141     /**
10142      * @cfg {String} maxText
10143      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10144      */
10145     maxText : "This date is after the maximum date",
10146     /**
10147      * @cfg {String} format
10148      * The default date format string which can be overriden for localization support.  The format must be
10149      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10150      */
10151     format : "m/d/y",
10152     /**
10153      * @cfg {Array} disabledDays
10154      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10155      */
10156     disabledDays : null,
10157     /**
10158      * @cfg {String} disabledDaysText
10159      * The tooltip to display when the date falls on a disabled day (defaults to "")
10160      */
10161     disabledDaysText : "",
10162     /**
10163      * @cfg {RegExp} disabledDatesRE
10164      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10165      */
10166     disabledDatesRE : null,
10167     /**
10168      * @cfg {String} disabledDatesText
10169      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10170      */
10171     disabledDatesText : "",
10172     /**
10173      * @cfg {Boolean} constrainToViewport
10174      * True to constrain the date picker to the viewport (defaults to true)
10175      */
10176     constrainToViewport : true,
10177     /**
10178      * @cfg {Array} monthNames
10179      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10180      */
10181     monthNames : Date.monthNames,
10182     /**
10183      * @cfg {Array} dayNames
10184      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10185      */
10186     dayNames : Date.dayNames,
10187     /**
10188      * @cfg {String} nextText
10189      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10190      */
10191     nextText: 'Next Month (Control+Right)',
10192     /**
10193      * @cfg {String} prevText
10194      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10195      */
10196     prevText: 'Previous Month (Control+Left)',
10197     /**
10198      * @cfg {String} monthYearText
10199      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10200      */
10201     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10202     /**
10203      * @cfg {Number} startDay
10204      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10205      */
10206     startDay : 0,
10207     /**
10208      * @cfg {Bool} showClear
10209      * Show a clear button (usefull for date form elements that can be blank.)
10210      */
10211     
10212     showClear: false,
10213     
10214     /**
10215      * Sets the value of the date field
10216      * @param {Date} value The date to set
10217      */
10218     setValue : function(value){
10219         var old = this.value;
10220         this.value = value.clearTime(true);
10221         if(this.el){
10222             this.update(this.value);
10223         }
10224     },
10225
10226     /**
10227      * Gets the current selected value of the date field
10228      * @return {Date} The selected date
10229      */
10230     getValue : function(){
10231         return this.value;
10232     },
10233
10234     // private
10235     focus : function(){
10236         if(this.el){
10237             this.update(this.activeDate);
10238         }
10239     },
10240
10241     // private
10242     onRender : function(container, position){
10243         var m = [
10244              '<table cellspacing="0">',
10245                 '<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>',
10246                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10247         var dn = this.dayNames;
10248         for(var i = 0; i < 7; i++){
10249             var d = this.startDay+i;
10250             if(d > 6){
10251                 d = d-7;
10252             }
10253             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10254         }
10255         m[m.length] = "</tr></thead><tbody><tr>";
10256         for(var i = 0; i < 42; i++) {
10257             if(i % 7 == 0 && i != 0){
10258                 m[m.length] = "</tr><tr>";
10259             }
10260             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10261         }
10262         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10263             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10264
10265         var el = document.createElement("div");
10266         el.className = "x-date-picker";
10267         el.innerHTML = m.join("");
10268
10269         container.dom.insertBefore(el, position);
10270
10271         this.el = Roo.get(el);
10272         this.eventEl = Roo.get(el.firstChild);
10273
10274         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10275             handler: this.showPrevMonth,
10276             scope: this,
10277             preventDefault:true,
10278             stopDefault:true
10279         });
10280
10281         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10282             handler: this.showNextMonth,
10283             scope: this,
10284             preventDefault:true,
10285             stopDefault:true
10286         });
10287
10288         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10289
10290         this.monthPicker = this.el.down('div.x-date-mp');
10291         this.monthPicker.enableDisplayMode('block');
10292         
10293         var kn = new Roo.KeyNav(this.eventEl, {
10294             "left" : function(e){
10295                 e.ctrlKey ?
10296                     this.showPrevMonth() :
10297                     this.update(this.activeDate.add("d", -1));
10298             },
10299
10300             "right" : function(e){
10301                 e.ctrlKey ?
10302                     this.showNextMonth() :
10303                     this.update(this.activeDate.add("d", 1));
10304             },
10305
10306             "up" : function(e){
10307                 e.ctrlKey ?
10308                     this.showNextYear() :
10309                     this.update(this.activeDate.add("d", -7));
10310             },
10311
10312             "down" : function(e){
10313                 e.ctrlKey ?
10314                     this.showPrevYear() :
10315                     this.update(this.activeDate.add("d", 7));
10316             },
10317
10318             "pageUp" : function(e){
10319                 this.showNextMonth();
10320             },
10321
10322             "pageDown" : function(e){
10323                 this.showPrevMonth();
10324             },
10325
10326             "enter" : function(e){
10327                 e.stopPropagation();
10328                 return true;
10329             },
10330
10331             scope : this
10332         });
10333
10334         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10335
10336         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10337
10338         this.el.unselectable();
10339         
10340         this.cells = this.el.select("table.x-date-inner tbody td");
10341         this.textNodes = this.el.query("table.x-date-inner tbody span");
10342
10343         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10344             text: "&#160;",
10345             tooltip: this.monthYearText
10346         });
10347
10348         this.mbtn.on('click', this.showMonthPicker, this);
10349         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10350
10351
10352         var today = (new Date()).dateFormat(this.format);
10353         
10354         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10355         if (this.showClear) {
10356             baseTb.add( new Roo.Toolbar.Fill());
10357         }
10358         baseTb.add({
10359             text: String.format(this.todayText, today),
10360             tooltip: String.format(this.todayTip, today),
10361             handler: this.selectToday,
10362             scope: this
10363         });
10364         
10365         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10366             
10367         //});
10368         if (this.showClear) {
10369             
10370             baseTb.add( new Roo.Toolbar.Fill());
10371             baseTb.add({
10372                 text: '&#160;',
10373                 cls: 'x-btn-icon x-btn-clear',
10374                 handler: function() {
10375                     //this.value = '';
10376                     this.fireEvent("select", this, '');
10377                 },
10378                 scope: this
10379             });
10380         }
10381         
10382         
10383         if(Roo.isIE){
10384             this.el.repaint();
10385         }
10386         this.update(this.value);
10387     },
10388
10389     createMonthPicker : function(){
10390         if(!this.monthPicker.dom.firstChild){
10391             var buf = ['<table border="0" cellspacing="0">'];
10392             for(var i = 0; i < 6; i++){
10393                 buf.push(
10394                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10395                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10396                     i == 0 ?
10397                     '<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>' :
10398                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10399                 );
10400             }
10401             buf.push(
10402                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10403                     this.okText,
10404                     '</button><button type="button" class="x-date-mp-cancel">',
10405                     this.cancelText,
10406                     '</button></td></tr>',
10407                 '</table>'
10408             );
10409             this.monthPicker.update(buf.join(''));
10410             this.monthPicker.on('click', this.onMonthClick, this);
10411             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10412
10413             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10414             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10415
10416             this.mpMonths.each(function(m, a, i){
10417                 i += 1;
10418                 if((i%2) == 0){
10419                     m.dom.xmonth = 5 + Math.round(i * .5);
10420                 }else{
10421                     m.dom.xmonth = Math.round((i-1) * .5);
10422                 }
10423             });
10424         }
10425     },
10426
10427     showMonthPicker : function(){
10428         this.createMonthPicker();
10429         var size = this.el.getSize();
10430         this.monthPicker.setSize(size);
10431         this.monthPicker.child('table').setSize(size);
10432
10433         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10434         this.updateMPMonth(this.mpSelMonth);
10435         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10436         this.updateMPYear(this.mpSelYear);
10437
10438         this.monthPicker.slideIn('t', {duration:.2});
10439     },
10440
10441     updateMPYear : function(y){
10442         this.mpyear = y;
10443         var ys = this.mpYears.elements;
10444         for(var i = 1; i <= 10; i++){
10445             var td = ys[i-1], y2;
10446             if((i%2) == 0){
10447                 y2 = y + Math.round(i * .5);
10448                 td.firstChild.innerHTML = y2;
10449                 td.xyear = y2;
10450             }else{
10451                 y2 = y - (5-Math.round(i * .5));
10452                 td.firstChild.innerHTML = y2;
10453                 td.xyear = y2;
10454             }
10455             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10456         }
10457     },
10458
10459     updateMPMonth : function(sm){
10460         this.mpMonths.each(function(m, a, i){
10461             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10462         });
10463     },
10464
10465     selectMPMonth: function(m){
10466         
10467     },
10468
10469     onMonthClick : function(e, t){
10470         e.stopEvent();
10471         var el = new Roo.Element(t), pn;
10472         if(el.is('button.x-date-mp-cancel')){
10473             this.hideMonthPicker();
10474         }
10475         else if(el.is('button.x-date-mp-ok')){
10476             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10477             this.hideMonthPicker();
10478         }
10479         else if(pn = el.up('td.x-date-mp-month', 2)){
10480             this.mpMonths.removeClass('x-date-mp-sel');
10481             pn.addClass('x-date-mp-sel');
10482             this.mpSelMonth = pn.dom.xmonth;
10483         }
10484         else if(pn = el.up('td.x-date-mp-year', 2)){
10485             this.mpYears.removeClass('x-date-mp-sel');
10486             pn.addClass('x-date-mp-sel');
10487             this.mpSelYear = pn.dom.xyear;
10488         }
10489         else if(el.is('a.x-date-mp-prev')){
10490             this.updateMPYear(this.mpyear-10);
10491         }
10492         else if(el.is('a.x-date-mp-next')){
10493             this.updateMPYear(this.mpyear+10);
10494         }
10495     },
10496
10497     onMonthDblClick : function(e, t){
10498         e.stopEvent();
10499         var el = new Roo.Element(t), pn;
10500         if(pn = el.up('td.x-date-mp-month', 2)){
10501             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10502             this.hideMonthPicker();
10503         }
10504         else if(pn = el.up('td.x-date-mp-year', 2)){
10505             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10506             this.hideMonthPicker();
10507         }
10508     },
10509
10510     hideMonthPicker : function(disableAnim){
10511         if(this.monthPicker){
10512             if(disableAnim === true){
10513                 this.monthPicker.hide();
10514             }else{
10515                 this.monthPicker.slideOut('t', {duration:.2});
10516             }
10517         }
10518     },
10519
10520     // private
10521     showPrevMonth : function(e){
10522         this.update(this.activeDate.add("mo", -1));
10523     },
10524
10525     // private
10526     showNextMonth : function(e){
10527         this.update(this.activeDate.add("mo", 1));
10528     },
10529
10530     // private
10531     showPrevYear : function(){
10532         this.update(this.activeDate.add("y", -1));
10533     },
10534
10535     // private
10536     showNextYear : function(){
10537         this.update(this.activeDate.add("y", 1));
10538     },
10539
10540     // private
10541     handleMouseWheel : function(e){
10542         var delta = e.getWheelDelta();
10543         if(delta > 0){
10544             this.showPrevMonth();
10545             e.stopEvent();
10546         } else if(delta < 0){
10547             this.showNextMonth();
10548             e.stopEvent();
10549         }
10550     },
10551
10552     // private
10553     handleDateClick : function(e, t){
10554         e.stopEvent();
10555         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10556             this.setValue(new Date(t.dateValue));
10557             this.fireEvent("select", this, this.value);
10558         }
10559     },
10560
10561     // private
10562     selectToday : function(){
10563         this.setValue(new Date().clearTime());
10564         this.fireEvent("select", this, this.value);
10565     },
10566
10567     // private
10568     update : function(date){
10569         var vd = this.activeDate;
10570         this.activeDate = date;
10571         if(vd && this.el){
10572             var t = date.getTime();
10573             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10574                 this.cells.removeClass("x-date-selected");
10575                 this.cells.each(function(c){
10576                    if(c.dom.firstChild.dateValue == t){
10577                        c.addClass("x-date-selected");
10578                        setTimeout(function(){
10579                             try{c.dom.firstChild.focus();}catch(e){}
10580                        }, 50);
10581                        return false;
10582                    }
10583                 });
10584                 return;
10585             }
10586         }
10587         var days = date.getDaysInMonth();
10588         var firstOfMonth = date.getFirstDateOfMonth();
10589         var startingPos = firstOfMonth.getDay()-this.startDay;
10590
10591         if(startingPos <= this.startDay){
10592             startingPos += 7;
10593         }
10594
10595         var pm = date.add("mo", -1);
10596         var prevStart = pm.getDaysInMonth()-startingPos;
10597
10598         var cells = this.cells.elements;
10599         var textEls = this.textNodes;
10600         days += startingPos;
10601
10602         // convert everything to numbers so it's fast
10603         var day = 86400000;
10604         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10605         var today = new Date().clearTime().getTime();
10606         var sel = date.clearTime().getTime();
10607         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10608         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10609         var ddMatch = this.disabledDatesRE;
10610         var ddText = this.disabledDatesText;
10611         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10612         var ddaysText = this.disabledDaysText;
10613         var format = this.format;
10614
10615         var setCellClass = function(cal, cell){
10616             cell.title = "";
10617             var t = d.getTime();
10618             cell.firstChild.dateValue = t;
10619             if(t == today){
10620                 cell.className += " x-date-today";
10621                 cell.title = cal.todayText;
10622             }
10623             if(t == sel){
10624                 cell.className += " x-date-selected";
10625                 setTimeout(function(){
10626                     try{cell.firstChild.focus();}catch(e){}
10627                 }, 50);
10628             }
10629             // disabling
10630             if(t < min) {
10631                 cell.className = " x-date-disabled";
10632                 cell.title = cal.minText;
10633                 return;
10634             }
10635             if(t > max) {
10636                 cell.className = " x-date-disabled";
10637                 cell.title = cal.maxText;
10638                 return;
10639             }
10640             if(ddays){
10641                 if(ddays.indexOf(d.getDay()) != -1){
10642                     cell.title = ddaysText;
10643                     cell.className = " x-date-disabled";
10644                 }
10645             }
10646             if(ddMatch && format){
10647                 var fvalue = d.dateFormat(format);
10648                 if(ddMatch.test(fvalue)){
10649                     cell.title = ddText.replace("%0", fvalue);
10650                     cell.className = " x-date-disabled";
10651                 }
10652             }
10653         };
10654
10655         var i = 0;
10656         for(; i < startingPos; i++) {
10657             textEls[i].innerHTML = (++prevStart);
10658             d.setDate(d.getDate()+1);
10659             cells[i].className = "x-date-prevday";
10660             setCellClass(this, cells[i]);
10661         }
10662         for(; i < days; i++){
10663             intDay = i - startingPos + 1;
10664             textEls[i].innerHTML = (intDay);
10665             d.setDate(d.getDate()+1);
10666             cells[i].className = "x-date-active";
10667             setCellClass(this, cells[i]);
10668         }
10669         var extraDays = 0;
10670         for(; i < 42; i++) {
10671              textEls[i].innerHTML = (++extraDays);
10672              d.setDate(d.getDate()+1);
10673              cells[i].className = "x-date-nextday";
10674              setCellClass(this, cells[i]);
10675         }
10676
10677         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10678
10679         if(!this.internalRender){
10680             var main = this.el.dom.firstChild;
10681             var w = main.offsetWidth;
10682             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10683             Roo.fly(main).setWidth(w);
10684             this.internalRender = true;
10685             // opera does not respect the auto grow header center column
10686             // then, after it gets a width opera refuses to recalculate
10687             // without a second pass
10688             if(Roo.isOpera && !this.secondPass){
10689                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10690                 this.secondPass = true;
10691                 this.update.defer(10, this, [date]);
10692             }
10693         }
10694     }
10695 });        /*
10696  * Based on:
10697  * Ext JS Library 1.1.1
10698  * Copyright(c) 2006-2007, Ext JS, LLC.
10699  *
10700  * Originally Released Under LGPL - original licence link has changed is not relivant.
10701  *
10702  * Fork - LGPL
10703  * <script type="text/javascript">
10704  */
10705 /**
10706  * @class Roo.TabPanel
10707  * @extends Roo.util.Observable
10708  * A lightweight tab container.
10709  * <br><br>
10710  * Usage:
10711  * <pre><code>
10712 // basic tabs 1, built from existing content
10713 var tabs = new Roo.TabPanel("tabs1");
10714 tabs.addTab("script", "View Script");
10715 tabs.addTab("markup", "View Markup");
10716 tabs.activate("script");
10717
10718 // more advanced tabs, built from javascript
10719 var jtabs = new Roo.TabPanel("jtabs");
10720 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10721
10722 // set up the UpdateManager
10723 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10724 var updater = tab2.getUpdateManager();
10725 updater.setDefaultUrl("ajax1.htm");
10726 tab2.on('activate', updater.refresh, updater, true);
10727
10728 // Use setUrl for Ajax loading
10729 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10730 tab3.setUrl("ajax2.htm", null, true);
10731
10732 // Disabled tab
10733 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10734 tab4.disable();
10735
10736 jtabs.activate("jtabs-1");
10737  * </code></pre>
10738  * @constructor
10739  * Create a new TabPanel.
10740  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10741  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10742  */
10743 Roo.TabPanel = function(container, config){
10744     /**
10745     * The container element for this TabPanel.
10746     * @type Roo.Element
10747     */
10748     this.el = Roo.get(container, true);
10749     if(config){
10750         if(typeof config == "boolean"){
10751             this.tabPosition = config ? "bottom" : "top";
10752         }else{
10753             Roo.apply(this, config);
10754         }
10755     }
10756     if(this.tabPosition == "bottom"){
10757         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10758         this.el.addClass("x-tabs-bottom");
10759     }
10760     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10761     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10762     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10763     if(Roo.isIE){
10764         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10765     }
10766     if(this.tabPosition != "bottom"){
10767         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
10768          * @type Roo.Element
10769          */
10770         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10771         this.el.addClass("x-tabs-top");
10772     }
10773     this.items = [];
10774
10775     this.bodyEl.setStyle("position", "relative");
10776
10777     this.active = null;
10778     this.activateDelegate = this.activate.createDelegate(this);
10779
10780     this.addEvents({
10781         /**
10782          * @event tabchange
10783          * Fires when the active tab changes
10784          * @param {Roo.TabPanel} this
10785          * @param {Roo.TabPanelItem} activePanel The new active tab
10786          */
10787         "tabchange": true,
10788         /**
10789          * @event beforetabchange
10790          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10791          * @param {Roo.TabPanel} this
10792          * @param {Object} e Set cancel to true on this object to cancel the tab change
10793          * @param {Roo.TabPanelItem} tab The tab being changed to
10794          */
10795         "beforetabchange" : true
10796     });
10797
10798     Roo.EventManager.onWindowResize(this.onResize, this);
10799     this.cpad = this.el.getPadding("lr");
10800     this.hiddenCount = 0;
10801
10802
10803     // toolbar on the tabbar support...
10804     if (this.toolbar) {
10805         var tcfg = this.toolbar;
10806         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
10807         this.toolbar = new Roo.Toolbar(tcfg);
10808         if (Roo.isSafari) {
10809             var tbl = tcfg.container.child('table', true);
10810             tbl.setAttribute('width', '100%');
10811         }
10812         
10813     }
10814    
10815
10816
10817     Roo.TabPanel.superclass.constructor.call(this);
10818 };
10819
10820 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10821     /*
10822      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10823      */
10824     tabPosition : "top",
10825     /*
10826      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10827      */
10828     currentTabWidth : 0,
10829     /*
10830      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10831      */
10832     minTabWidth : 40,
10833     /*
10834      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10835      */
10836     maxTabWidth : 250,
10837     /*
10838      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10839      */
10840     preferredTabWidth : 175,
10841     /*
10842      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10843      */
10844     resizeTabs : false,
10845     /*
10846      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10847      */
10848     monitorResize : true,
10849     /*
10850      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
10851      */
10852     toolbar : false,
10853
10854     /**
10855      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10856      * @param {String} id The id of the div to use <b>or create</b>
10857      * @param {String} text The text for the tab
10858      * @param {String} content (optional) Content to put in the TabPanelItem body
10859      * @param {Boolean} closable (optional) True to create a close icon on the tab
10860      * @return {Roo.TabPanelItem} The created TabPanelItem
10861      */
10862     addTab : function(id, text, content, closable){
10863         var item = new Roo.TabPanelItem(this, id, text, closable);
10864         this.addTabItem(item);
10865         if(content){
10866             item.setContent(content);
10867         }
10868         return item;
10869     },
10870
10871     /**
10872      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10873      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10874      * @return {Roo.TabPanelItem}
10875      */
10876     getTab : function(id){
10877         return this.items[id];
10878     },
10879
10880     /**
10881      * Hides the {@link Roo.TabPanelItem} with the specified id/index
10882      * @param {String/Number} id The id or index of the TabPanelItem to hide.
10883      */
10884     hideTab : function(id){
10885         var t = this.items[id];
10886         if(!t.isHidden()){
10887            t.setHidden(true);
10888            this.hiddenCount++;
10889            this.autoSizeTabs();
10890         }
10891     },
10892
10893     /**
10894      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10895      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10896      */
10897     unhideTab : function(id){
10898         var t = this.items[id];
10899         if(t.isHidden()){
10900            t.setHidden(false);
10901            this.hiddenCount--;
10902            this.autoSizeTabs();
10903         }
10904     },
10905
10906     /**
10907      * Adds an existing {@link Roo.TabPanelItem}.
10908      * @param {Roo.TabPanelItem} item The TabPanelItem to add
10909      */
10910     addTabItem : function(item){
10911         this.items[item.id] = item;
10912         this.items.push(item);
10913         if(this.resizeTabs){
10914            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10915            this.autoSizeTabs();
10916         }else{
10917             item.autoSize();
10918         }
10919     },
10920
10921     /**
10922      * Removes a {@link Roo.TabPanelItem}.
10923      * @param {String/Number} id The id or index of the TabPanelItem to remove.
10924      */
10925     removeTab : function(id){
10926         var items = this.items;
10927         var tab = items[id];
10928         if(!tab) { return; }
10929         var index = items.indexOf(tab);
10930         if(this.active == tab && items.length > 1){
10931             var newTab = this.getNextAvailable(index);
10932             if(newTab) {
10933                 newTab.activate();
10934             }
10935         }
10936         this.stripEl.dom.removeChild(tab.pnode.dom);
10937         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
10938             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
10939         }
10940         items.splice(index, 1);
10941         delete this.items[tab.id];
10942         tab.fireEvent("close", tab);
10943         tab.purgeListeners();
10944         this.autoSizeTabs();
10945     },
10946
10947     getNextAvailable : function(start){
10948         var items = this.items;
10949         var index = start;
10950         // look for a next tab that will slide over to
10951         // replace the one being removed
10952         while(index < items.length){
10953             var item = items[++index];
10954             if(item && !item.isHidden()){
10955                 return item;
10956             }
10957         }
10958         // if one isn't found select the previous tab (on the left)
10959         index = start;
10960         while(index >= 0){
10961             var item = items[--index];
10962             if(item && !item.isHidden()){
10963                 return item;
10964             }
10965         }
10966         return null;
10967     },
10968
10969     /**
10970      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
10971      * @param {String/Number} id The id or index of the TabPanelItem to disable.
10972      */
10973     disableTab : function(id){
10974         var tab = this.items[id];
10975         if(tab && this.active != tab){
10976             tab.disable();
10977         }
10978     },
10979
10980     /**
10981      * Enables a {@link Roo.TabPanelItem} that is disabled.
10982      * @param {String/Number} id The id or index of the TabPanelItem to enable.
10983      */
10984     enableTab : function(id){
10985         var tab = this.items[id];
10986         tab.enable();
10987     },
10988
10989     /**
10990      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
10991      * @param {String/Number} id The id or index of the TabPanelItem to activate.
10992      * @return {Roo.TabPanelItem} The TabPanelItem.
10993      */
10994     activate : function(id){
10995         var tab = this.items[id];
10996         if(!tab){
10997             return null;
10998         }
10999         if(tab == this.active || tab.disabled){
11000             return tab;
11001         }
11002         var e = {};
11003         this.fireEvent("beforetabchange", this, e, tab);
11004         if(e.cancel !== true && !tab.disabled){
11005             if(this.active){
11006                 this.active.hide();
11007             }
11008             this.active = this.items[id];
11009             this.active.show();
11010             this.fireEvent("tabchange", this, this.active);
11011         }
11012         return tab;
11013     },
11014
11015     /**
11016      * Gets the active {@link Roo.TabPanelItem}.
11017      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
11018      */
11019     getActiveTab : function(){
11020         return this.active;
11021     },
11022
11023     /**
11024      * Updates the tab body element to fit the height of the container element
11025      * for overflow scrolling
11026      * @param {Number} targetHeight (optional) Override the starting height from the elements height
11027      */
11028     syncHeight : function(targetHeight){
11029         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
11030         var bm = this.bodyEl.getMargins();
11031         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
11032         this.bodyEl.setHeight(newHeight);
11033         return newHeight;
11034     },
11035
11036     onResize : function(){
11037         if(this.monitorResize){
11038             this.autoSizeTabs();
11039         }
11040     },
11041
11042     /**
11043      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
11044      */
11045     beginUpdate : function(){
11046         this.updating = true;
11047     },
11048
11049     /**
11050      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
11051      */
11052     endUpdate : function(){
11053         this.updating = false;
11054         this.autoSizeTabs();
11055     },
11056
11057     /**
11058      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
11059      */
11060     autoSizeTabs : function(){
11061         var count = this.items.length;
11062         var vcount = count - this.hiddenCount;
11063         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
11064         var w = Math.max(this.el.getWidth() - this.cpad, 10);
11065         var availWidth = Math.floor(w / vcount);
11066         var b = this.stripBody;
11067         if(b.getWidth() > w){
11068             var tabs = this.items;
11069             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
11070             if(availWidth < this.minTabWidth){
11071                 /*if(!this.sleft){    // incomplete scrolling code
11072                     this.createScrollButtons();
11073                 }
11074                 this.showScroll();
11075                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
11076             }
11077         }else{
11078             if(this.currentTabWidth < this.preferredTabWidth){
11079                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
11080             }
11081         }
11082     },
11083
11084     /**
11085      * Returns the number of tabs in this TabPanel.
11086      * @return {Number}
11087      */
11088      getCount : function(){
11089          return this.items.length;
11090      },
11091
11092     /**
11093      * Resizes all the tabs to the passed width
11094      * @param {Number} The new width
11095      */
11096     setTabWidth : function(width){
11097         this.currentTabWidth = width;
11098         for(var i = 0, len = this.items.length; i < len; i++) {
11099                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
11100         }
11101     },
11102
11103     /**
11104      * Destroys this TabPanel
11105      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
11106      */
11107     destroy : function(removeEl){
11108         Roo.EventManager.removeResizeListener(this.onResize, this);
11109         for(var i = 0, len = this.items.length; i < len; i++){
11110             this.items[i].purgeListeners();
11111         }
11112         if(removeEl === true){
11113             this.el.update("");
11114             this.el.remove();
11115         }
11116     }
11117 });
11118
11119 /**
11120  * @class Roo.TabPanelItem
11121  * @extends Roo.util.Observable
11122  * Represents an individual item (tab plus body) in a TabPanel.
11123  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11124  * @param {String} id The id of this TabPanelItem
11125  * @param {String} text The text for the tab of this TabPanelItem
11126  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11127  */
11128 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11129     /**
11130      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11131      * @type Roo.TabPanel
11132      */
11133     this.tabPanel = tabPanel;
11134     /**
11135      * The id for this TabPanelItem
11136      * @type String
11137      */
11138     this.id = id;
11139     /** @private */
11140     this.disabled = false;
11141     /** @private */
11142     this.text = text;
11143     /** @private */
11144     this.loaded = false;
11145     this.closable = closable;
11146
11147     /**
11148      * The body element for this TabPanelItem.
11149      * @type Roo.Element
11150      */
11151     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11152     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11153     this.bodyEl.setStyle("display", "block");
11154     this.bodyEl.setStyle("zoom", "1");
11155     this.hideAction();
11156
11157     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11158     /** @private */
11159     this.el = Roo.get(els.el, true);
11160     this.inner = Roo.get(els.inner, true);
11161     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11162     this.pnode = Roo.get(els.el.parentNode, true);
11163     this.el.on("mousedown", this.onTabMouseDown, this);
11164     this.el.on("click", this.onTabClick, this);
11165     /** @private */
11166     if(closable){
11167         var c = Roo.get(els.close, true);
11168         c.dom.title = this.closeText;
11169         c.addClassOnOver("close-over");
11170         c.on("click", this.closeClick, this);
11171      }
11172
11173     this.addEvents({
11174          /**
11175          * @event activate
11176          * Fires when this tab becomes the active tab.
11177          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11178          * @param {Roo.TabPanelItem} this
11179          */
11180         "activate": true,
11181         /**
11182          * @event beforeclose
11183          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11184          * @param {Roo.TabPanelItem} this
11185          * @param {Object} e Set cancel to true on this object to cancel the close.
11186          */
11187         "beforeclose": true,
11188         /**
11189          * @event close
11190          * Fires when this tab is closed.
11191          * @param {Roo.TabPanelItem} this
11192          */
11193          "close": true,
11194         /**
11195          * @event deactivate
11196          * Fires when this tab is no longer the active tab.
11197          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11198          * @param {Roo.TabPanelItem} this
11199          */
11200          "deactivate" : true
11201     });
11202     this.hidden = false;
11203
11204     Roo.TabPanelItem.superclass.constructor.call(this);
11205 };
11206
11207 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11208     purgeListeners : function(){
11209        Roo.util.Observable.prototype.purgeListeners.call(this);
11210        this.el.removeAllListeners();
11211     },
11212     /**
11213      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11214      */
11215     show : function(){
11216         this.pnode.addClass("on");
11217         this.showAction();
11218         if(Roo.isOpera){
11219             this.tabPanel.stripWrap.repaint();
11220         }
11221         this.fireEvent("activate", this.tabPanel, this);
11222     },
11223
11224     /**
11225      * Returns true if this tab is the active tab.
11226      * @return {Boolean}
11227      */
11228     isActive : function(){
11229         return this.tabPanel.getActiveTab() == this;
11230     },
11231
11232     /**
11233      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11234      */
11235     hide : function(){
11236         this.pnode.removeClass("on");
11237         this.hideAction();
11238         this.fireEvent("deactivate", this.tabPanel, this);
11239     },
11240
11241     hideAction : function(){
11242         this.bodyEl.hide();
11243         this.bodyEl.setStyle("position", "absolute");
11244         this.bodyEl.setLeft("-20000px");
11245         this.bodyEl.setTop("-20000px");
11246     },
11247
11248     showAction : function(){
11249         this.bodyEl.setStyle("position", "relative");
11250         this.bodyEl.setTop("");
11251         this.bodyEl.setLeft("");
11252         this.bodyEl.show();
11253     },
11254
11255     /**
11256      * Set the tooltip for the tab.
11257      * @param {String} tooltip The tab's tooltip
11258      */
11259     setTooltip : function(text){
11260         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11261             this.textEl.dom.qtip = text;
11262             this.textEl.dom.removeAttribute('title');
11263         }else{
11264             this.textEl.dom.title = text;
11265         }
11266     },
11267
11268     onTabClick : function(e){
11269         e.preventDefault();
11270         this.tabPanel.activate(this.id);
11271     },
11272
11273     onTabMouseDown : function(e){
11274         e.preventDefault();
11275         this.tabPanel.activate(this.id);
11276     },
11277
11278     getWidth : function(){
11279         return this.inner.getWidth();
11280     },
11281
11282     setWidth : function(width){
11283         var iwidth = width - this.pnode.getPadding("lr");
11284         this.inner.setWidth(iwidth);
11285         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11286         this.pnode.setWidth(width);
11287     },
11288
11289     /**
11290      * Show or hide the tab
11291      * @param {Boolean} hidden True to hide or false to show.
11292      */
11293     setHidden : function(hidden){
11294         this.hidden = hidden;
11295         this.pnode.setStyle("display", hidden ? "none" : "");
11296     },
11297
11298     /**
11299      * Returns true if this tab is "hidden"
11300      * @return {Boolean}
11301      */
11302     isHidden : function(){
11303         return this.hidden;
11304     },
11305
11306     /**
11307      * Returns the text for this tab
11308      * @return {String}
11309      */
11310     getText : function(){
11311         return this.text;
11312     },
11313
11314     autoSize : function(){
11315         //this.el.beginMeasure();
11316         this.textEl.setWidth(1);
11317         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11318         //this.el.endMeasure();
11319     },
11320
11321     /**
11322      * Sets the text for the tab (Note: this also sets the tooltip text)
11323      * @param {String} text The tab's text and tooltip
11324      */
11325     setText : function(text){
11326         this.text = text;
11327         this.textEl.update(text);
11328         this.setTooltip(text);
11329         if(!this.tabPanel.resizeTabs){
11330             this.autoSize();
11331         }
11332     },
11333     /**
11334      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11335      */
11336     activate : function(){
11337         this.tabPanel.activate(this.id);
11338     },
11339
11340     /**
11341      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11342      */
11343     disable : function(){
11344         if(this.tabPanel.active != this){
11345             this.disabled = true;
11346             this.pnode.addClass("disabled");
11347         }
11348     },
11349
11350     /**
11351      * Enables this TabPanelItem if it was previously disabled.
11352      */
11353     enable : function(){
11354         this.disabled = false;
11355         this.pnode.removeClass("disabled");
11356     },
11357
11358     /**
11359      * Sets the content for this TabPanelItem.
11360      * @param {String} content The content
11361      * @param {Boolean} loadScripts true to look for and load scripts
11362      */
11363     setContent : function(content, loadScripts){
11364         this.bodyEl.update(content, loadScripts);
11365     },
11366
11367     /**
11368      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11369      * @return {Roo.UpdateManager} The UpdateManager
11370      */
11371     getUpdateManager : function(){
11372         return this.bodyEl.getUpdateManager();
11373     },
11374
11375     /**
11376      * Set a URL to be used to load the content for this TabPanelItem.
11377      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11378      * @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)
11379      * @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)
11380      * @return {Roo.UpdateManager} The UpdateManager
11381      */
11382     setUrl : function(url, params, loadOnce){
11383         if(this.refreshDelegate){
11384             this.un('activate', this.refreshDelegate);
11385         }
11386         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11387         this.on("activate", this.refreshDelegate);
11388         return this.bodyEl.getUpdateManager();
11389     },
11390
11391     /** @private */
11392     _handleRefresh : function(url, params, loadOnce){
11393         if(!loadOnce || !this.loaded){
11394             var updater = this.bodyEl.getUpdateManager();
11395             updater.update(url, params, this._setLoaded.createDelegate(this));
11396         }
11397     },
11398
11399     /**
11400      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11401      *   Will fail silently if the setUrl method has not been called.
11402      *   This does not activate the panel, just updates its content.
11403      */
11404     refresh : function(){
11405         if(this.refreshDelegate){
11406            this.loaded = false;
11407            this.refreshDelegate();
11408         }
11409     },
11410
11411     /** @private */
11412     _setLoaded : function(){
11413         this.loaded = true;
11414     },
11415
11416     /** @private */
11417     closeClick : function(e){
11418         var o = {};
11419         e.stopEvent();
11420         this.fireEvent("beforeclose", this, o);
11421         if(o.cancel !== true){
11422             this.tabPanel.removeTab(this.id);
11423         }
11424     },
11425     /**
11426      * The text displayed in the tooltip for the close icon.
11427      * @type String
11428      */
11429     closeText : "Close this tab"
11430 });
11431
11432 /** @private */
11433 Roo.TabPanel.prototype.createStrip = function(container){
11434     var strip = document.createElement("div");
11435     strip.className = "x-tabs-wrap";
11436     container.appendChild(strip);
11437     return strip;
11438 };
11439 /** @private */
11440 Roo.TabPanel.prototype.createStripList = function(strip){
11441     // div wrapper for retard IE
11442     // returns the "tr" element.
11443     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
11444         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
11445         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
11446     return strip.firstChild.firstChild.firstChild.firstChild;
11447 };
11448 /** @private */
11449 Roo.TabPanel.prototype.createBody = function(container){
11450     var body = document.createElement("div");
11451     Roo.id(body, "tab-body");
11452     Roo.fly(body).addClass("x-tabs-body");
11453     container.appendChild(body);
11454     return body;
11455 };
11456 /** @private */
11457 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11458     var body = Roo.getDom(id);
11459     if(!body){
11460         body = document.createElement("div");
11461         body.id = id;
11462     }
11463     Roo.fly(body).addClass("x-tabs-item-body");
11464     bodyEl.insertBefore(body, bodyEl.firstChild);
11465     return body;
11466 };
11467 /** @private */
11468 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11469     var td = document.createElement("td");
11470     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
11471     //stripEl.appendChild(td);
11472     if(closable){
11473         td.className = "x-tabs-closable";
11474         if(!this.closeTpl){
11475             this.closeTpl = new Roo.Template(
11476                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11477                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11478                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11479             );
11480         }
11481         var el = this.closeTpl.overwrite(td, {"text": text});
11482         var close = el.getElementsByTagName("div")[0];
11483         var inner = el.getElementsByTagName("em")[0];
11484         return {"el": el, "close": close, "inner": inner};
11485     } else {
11486         if(!this.tabTpl){
11487             this.tabTpl = new Roo.Template(
11488                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11489                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11490             );
11491         }
11492         var el = this.tabTpl.overwrite(td, {"text": text});
11493         var inner = el.getElementsByTagName("em")[0];
11494         return {"el": el, "inner": inner};
11495     }
11496 };/*
11497  * Based on:
11498  * Ext JS Library 1.1.1
11499  * Copyright(c) 2006-2007, Ext JS, LLC.
11500  *
11501  * Originally Released Under LGPL - original licence link has changed is not relivant.
11502  *
11503  * Fork - LGPL
11504  * <script type="text/javascript">
11505  */
11506
11507 /**
11508  * @class Roo.Button
11509  * @extends Roo.util.Observable
11510  * Simple Button class
11511  * @cfg {String} text The button text
11512  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11513  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11514  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11515  * @cfg {Object} scope The scope of the handler
11516  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11517  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11518  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11519  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11520  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11521  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11522    applies if enableToggle = true)
11523  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11524  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11525   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11526  * @constructor
11527  * Create a new button
11528  * @param {Object} config The config object
11529  */
11530 Roo.Button = function(renderTo, config)
11531 {
11532     if (!config) {
11533         config = renderTo;
11534         renderTo = config.renderTo || false;
11535     }
11536     
11537     Roo.apply(this, config);
11538     this.addEvents({
11539         /**
11540              * @event click
11541              * Fires when this button is clicked
11542              * @param {Button} this
11543              * @param {EventObject} e The click event
11544              */
11545             "click" : true,
11546         /**
11547              * @event toggle
11548              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11549              * @param {Button} this
11550              * @param {Boolean} pressed
11551              */
11552             "toggle" : true,
11553         /**
11554              * @event mouseover
11555              * Fires when the mouse hovers over the button
11556              * @param {Button} this
11557              * @param {Event} e The event object
11558              */
11559         'mouseover' : true,
11560         /**
11561              * @event mouseout
11562              * Fires when the mouse exits the button
11563              * @param {Button} this
11564              * @param {Event} e The event object
11565              */
11566         'mouseout': true,
11567          /**
11568              * @event render
11569              * Fires when the button is rendered
11570              * @param {Button} this
11571              */
11572         'render': true
11573     });
11574     if(this.menu){
11575         this.menu = Roo.menu.MenuMgr.get(this.menu);
11576     }
11577     // register listeners first!!  - so render can be captured..
11578     Roo.util.Observable.call(this);
11579     if(renderTo){
11580         this.render(renderTo);
11581     }
11582     
11583   
11584 };
11585
11586 Roo.extend(Roo.Button, Roo.util.Observable, {
11587     /**
11588      * 
11589      */
11590     
11591     /**
11592      * Read-only. True if this button is hidden
11593      * @type Boolean
11594      */
11595     hidden : false,
11596     /**
11597      * Read-only. True if this button is disabled
11598      * @type Boolean
11599      */
11600     disabled : false,
11601     /**
11602      * Read-only. True if this button is pressed (only if enableToggle = true)
11603      * @type Boolean
11604      */
11605     pressed : false,
11606
11607     /**
11608      * @cfg {Number} tabIndex 
11609      * The DOM tabIndex for this button (defaults to undefined)
11610      */
11611     tabIndex : undefined,
11612
11613     /**
11614      * @cfg {Boolean} enableToggle
11615      * True to enable pressed/not pressed toggling (defaults to false)
11616      */
11617     enableToggle: false,
11618     /**
11619      * @cfg {Mixed} menu
11620      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11621      */
11622     menu : undefined,
11623     /**
11624      * @cfg {String} menuAlign
11625      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11626      */
11627     menuAlign : "tl-bl?",
11628
11629     /**
11630      * @cfg {String} iconCls
11631      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11632      */
11633     iconCls : undefined,
11634     /**
11635      * @cfg {String} type
11636      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11637      */
11638     type : 'button',
11639
11640     // private
11641     menuClassTarget: 'tr',
11642
11643     /**
11644      * @cfg {String} clickEvent
11645      * The type of event to map to the button's event handler (defaults to 'click')
11646      */
11647     clickEvent : 'click',
11648
11649     /**
11650      * @cfg {Boolean} handleMouseEvents
11651      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11652      */
11653     handleMouseEvents : true,
11654
11655     /**
11656      * @cfg {String} tooltipType
11657      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11658      */
11659     tooltipType : 'qtip',
11660
11661     /**
11662      * @cfg {String} cls
11663      * A CSS class to apply to the button's main element.
11664      */
11665     
11666     /**
11667      * @cfg {Roo.Template} template (Optional)
11668      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11669      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11670      * require code modifications if required elements (e.g. a button) aren't present.
11671      */
11672
11673     // private
11674     render : function(renderTo){
11675         var btn;
11676         if(this.hideParent){
11677             this.parentEl = Roo.get(renderTo);
11678         }
11679         if(!this.dhconfig){
11680             if(!this.template){
11681                 if(!Roo.Button.buttonTemplate){
11682                     // hideous table template
11683                     Roo.Button.buttonTemplate = new Roo.Template(
11684                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11685                         '<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>',
11686                         "</tr></tbody></table>");
11687                 }
11688                 this.template = Roo.Button.buttonTemplate;
11689             }
11690             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11691             var btnEl = btn.child("button:first");
11692             btnEl.on('focus', this.onFocus, this);
11693             btnEl.on('blur', this.onBlur, this);
11694             if(this.cls){
11695                 btn.addClass(this.cls);
11696             }
11697             if(this.icon){
11698                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11699             }
11700             if(this.iconCls){
11701                 btnEl.addClass(this.iconCls);
11702                 if(!this.cls){
11703                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11704                 }
11705             }
11706             if(this.tabIndex !== undefined){
11707                 btnEl.dom.tabIndex = this.tabIndex;
11708             }
11709             if(this.tooltip){
11710                 if(typeof this.tooltip == 'object'){
11711                     Roo.QuickTips.tips(Roo.apply({
11712                           target: btnEl.id
11713                     }, this.tooltip));
11714                 } else {
11715                     btnEl.dom[this.tooltipType] = this.tooltip;
11716                 }
11717             }
11718         }else{
11719             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11720         }
11721         this.el = btn;
11722         if(this.id){
11723             this.el.dom.id = this.el.id = this.id;
11724         }
11725         if(this.menu){
11726             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11727             this.menu.on("show", this.onMenuShow, this);
11728             this.menu.on("hide", this.onMenuHide, this);
11729         }
11730         btn.addClass("x-btn");
11731         if(Roo.isIE && !Roo.isIE7){
11732             this.autoWidth.defer(1, this);
11733         }else{
11734             this.autoWidth();
11735         }
11736         if(this.handleMouseEvents){
11737             btn.on("mouseover", this.onMouseOver, this);
11738             btn.on("mouseout", this.onMouseOut, this);
11739             btn.on("mousedown", this.onMouseDown, this);
11740         }
11741         btn.on(this.clickEvent, this.onClick, this);
11742         //btn.on("mouseup", this.onMouseUp, this);
11743         if(this.hidden){
11744             this.hide();
11745         }
11746         if(this.disabled){
11747             this.disable();
11748         }
11749         Roo.ButtonToggleMgr.register(this);
11750         if(this.pressed){
11751             this.el.addClass("x-btn-pressed");
11752         }
11753         if(this.repeat){
11754             var repeater = new Roo.util.ClickRepeater(btn,
11755                 typeof this.repeat == "object" ? this.repeat : {}
11756             );
11757             repeater.on("click", this.onClick,  this);
11758         }
11759         
11760         this.fireEvent('render', this);
11761         
11762     },
11763     /**
11764      * Returns the button's underlying element
11765      * @return {Roo.Element} The element
11766      */
11767     getEl : function(){
11768         return this.el;  
11769     },
11770     
11771     /**
11772      * Destroys this Button and removes any listeners.
11773      */
11774     destroy : function(){
11775         Roo.ButtonToggleMgr.unregister(this);
11776         this.el.removeAllListeners();
11777         this.purgeListeners();
11778         this.el.remove();
11779     },
11780
11781     // private
11782     autoWidth : function(){
11783         if(this.el){
11784             this.el.setWidth("auto");
11785             if(Roo.isIE7 && Roo.isStrict){
11786                 var ib = this.el.child('button');
11787                 if(ib && ib.getWidth() > 20){
11788                     ib.clip();
11789                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11790                 }
11791             }
11792             if(this.minWidth){
11793                 if(this.hidden){
11794                     this.el.beginMeasure();
11795                 }
11796                 if(this.el.getWidth() < this.minWidth){
11797                     this.el.setWidth(this.minWidth);
11798                 }
11799                 if(this.hidden){
11800                     this.el.endMeasure();
11801                 }
11802             }
11803         }
11804     },
11805
11806     /**
11807      * Assigns this button's click handler
11808      * @param {Function} handler The function to call when the button is clicked
11809      * @param {Object} scope (optional) Scope for the function passed in
11810      */
11811     setHandler : function(handler, scope){
11812         this.handler = handler;
11813         this.scope = scope;  
11814     },
11815     
11816     /**
11817      * Sets this button's text
11818      * @param {String} text The button text
11819      */
11820     setText : function(text){
11821         this.text = text;
11822         if(this.el){
11823             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11824         }
11825         this.autoWidth();
11826     },
11827     
11828     /**
11829      * Gets the text for this button
11830      * @return {String} The button text
11831      */
11832     getText : function(){
11833         return this.text;  
11834     },
11835     
11836     /**
11837      * Show this button
11838      */
11839     show: function(){
11840         this.hidden = false;
11841         if(this.el){
11842             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11843         }
11844     },
11845     
11846     /**
11847      * Hide this button
11848      */
11849     hide: function(){
11850         this.hidden = true;
11851         if(this.el){
11852             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11853         }
11854     },
11855     
11856     /**
11857      * Convenience function for boolean show/hide
11858      * @param {Boolean} visible True to show, false to hide
11859      */
11860     setVisible: function(visible){
11861         if(visible) {
11862             this.show();
11863         }else{
11864             this.hide();
11865         }
11866     },
11867     
11868     /**
11869      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11870      * @param {Boolean} state (optional) Force a particular state
11871      */
11872     toggle : function(state){
11873         state = state === undefined ? !this.pressed : state;
11874         if(state != this.pressed){
11875             if(state){
11876                 this.el.addClass("x-btn-pressed");
11877                 this.pressed = true;
11878                 this.fireEvent("toggle", this, true);
11879             }else{
11880                 this.el.removeClass("x-btn-pressed");
11881                 this.pressed = false;
11882                 this.fireEvent("toggle", this, false);
11883             }
11884             if(this.toggleHandler){
11885                 this.toggleHandler.call(this.scope || this, this, state);
11886             }
11887         }
11888     },
11889     
11890     /**
11891      * Focus the button
11892      */
11893     focus : function(){
11894         this.el.child('button:first').focus();
11895     },
11896     
11897     /**
11898      * Disable this button
11899      */
11900     disable : function(){
11901         if(this.el){
11902             this.el.addClass("x-btn-disabled");
11903         }
11904         this.disabled = true;
11905     },
11906     
11907     /**
11908      * Enable this button
11909      */
11910     enable : function(){
11911         if(this.el){
11912             this.el.removeClass("x-btn-disabled");
11913         }
11914         this.disabled = false;
11915     },
11916
11917     /**
11918      * Convenience function for boolean enable/disable
11919      * @param {Boolean} enabled True to enable, false to disable
11920      */
11921     setDisabled : function(v){
11922         this[v !== true ? "enable" : "disable"]();
11923     },
11924
11925     // private
11926     onClick : function(e){
11927         if(e){
11928             e.preventDefault();
11929         }
11930         if(e.button != 0){
11931             return;
11932         }
11933         if(!this.disabled){
11934             if(this.enableToggle){
11935                 this.toggle();
11936             }
11937             if(this.menu && !this.menu.isVisible()){
11938                 this.menu.show(this.el, this.menuAlign);
11939             }
11940             this.fireEvent("click", this, e);
11941             if(this.handler){
11942                 this.el.removeClass("x-btn-over");
11943                 this.handler.call(this.scope || this, this, e);
11944             }
11945         }
11946     },
11947     // private
11948     onMouseOver : function(e){
11949         if(!this.disabled){
11950             this.el.addClass("x-btn-over");
11951             this.fireEvent('mouseover', this, e);
11952         }
11953     },
11954     // private
11955     onMouseOut : function(e){
11956         if(!e.within(this.el,  true)){
11957             this.el.removeClass("x-btn-over");
11958             this.fireEvent('mouseout', this, e);
11959         }
11960     },
11961     // private
11962     onFocus : function(e){
11963         if(!this.disabled){
11964             this.el.addClass("x-btn-focus");
11965         }
11966     },
11967     // private
11968     onBlur : function(e){
11969         this.el.removeClass("x-btn-focus");
11970     },
11971     // private
11972     onMouseDown : function(e){
11973         if(!this.disabled && e.button == 0){
11974             this.el.addClass("x-btn-click");
11975             Roo.get(document).on('mouseup', this.onMouseUp, this);
11976         }
11977     },
11978     // private
11979     onMouseUp : function(e){
11980         if(e.button == 0){
11981             this.el.removeClass("x-btn-click");
11982             Roo.get(document).un('mouseup', this.onMouseUp, this);
11983         }
11984     },
11985     // private
11986     onMenuShow : function(e){
11987         this.el.addClass("x-btn-menu-active");
11988     },
11989     // private
11990     onMenuHide : function(e){
11991         this.el.removeClass("x-btn-menu-active");
11992     }   
11993 });
11994
11995 // Private utility class used by Button
11996 Roo.ButtonToggleMgr = function(){
11997    var groups = {};
11998    
11999    function toggleGroup(btn, state){
12000        if(state){
12001            var g = groups[btn.toggleGroup];
12002            for(var i = 0, l = g.length; i < l; i++){
12003                if(g[i] != btn){
12004                    g[i].toggle(false);
12005                }
12006            }
12007        }
12008    }
12009    
12010    return {
12011        register : function(btn){
12012            if(!btn.toggleGroup){
12013                return;
12014            }
12015            var g = groups[btn.toggleGroup];
12016            if(!g){
12017                g = groups[btn.toggleGroup] = [];
12018            }
12019            g.push(btn);
12020            btn.on("toggle", toggleGroup);
12021        },
12022        
12023        unregister : function(btn){
12024            if(!btn.toggleGroup){
12025                return;
12026            }
12027            var g = groups[btn.toggleGroup];
12028            if(g){
12029                g.remove(btn);
12030                btn.un("toggle", toggleGroup);
12031            }
12032        }
12033    };
12034 }();/*
12035  * Based on:
12036  * Ext JS Library 1.1.1
12037  * Copyright(c) 2006-2007, Ext JS, LLC.
12038  *
12039  * Originally Released Under LGPL - original licence link has changed is not relivant.
12040  *
12041  * Fork - LGPL
12042  * <script type="text/javascript">
12043  */
12044  
12045 /**
12046  * @class Roo.SplitButton
12047  * @extends Roo.Button
12048  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
12049  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
12050  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
12051  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
12052  * @cfg {String} arrowTooltip The title attribute of the arrow
12053  * @constructor
12054  * Create a new menu button
12055  * @param {String/HTMLElement/Element} renderTo The element to append the button to
12056  * @param {Object} config The config object
12057  */
12058 Roo.SplitButton = function(renderTo, config){
12059     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
12060     /**
12061      * @event arrowclick
12062      * Fires when this button's arrow is clicked
12063      * @param {SplitButton} this
12064      * @param {EventObject} e The click event
12065      */
12066     this.addEvents({"arrowclick":true});
12067 };
12068
12069 Roo.extend(Roo.SplitButton, Roo.Button, {
12070     render : function(renderTo){
12071         // this is one sweet looking template!
12072         var tpl = new Roo.Template(
12073             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
12074             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
12075             '<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>',
12076             "</tbody></table></td><td>",
12077             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
12078             '<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>',
12079             "</tbody></table></td></tr></table>"
12080         );
12081         var btn = tpl.append(renderTo, [this.text, this.type], true);
12082         var btnEl = btn.child("button");
12083         if(this.cls){
12084             btn.addClass(this.cls);
12085         }
12086         if(this.icon){
12087             btnEl.setStyle('background-image', 'url(' +this.icon +')');
12088         }
12089         if(this.iconCls){
12090             btnEl.addClass(this.iconCls);
12091             if(!this.cls){
12092                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
12093             }
12094         }
12095         this.el = btn;
12096         if(this.handleMouseEvents){
12097             btn.on("mouseover", this.onMouseOver, this);
12098             btn.on("mouseout", this.onMouseOut, this);
12099             btn.on("mousedown", this.onMouseDown, this);
12100             btn.on("mouseup", this.onMouseUp, this);
12101         }
12102         btn.on(this.clickEvent, this.onClick, this);
12103         if(this.tooltip){
12104             if(typeof this.tooltip == 'object'){
12105                 Roo.QuickTips.tips(Roo.apply({
12106                       target: btnEl.id
12107                 }, this.tooltip));
12108             } else {
12109                 btnEl.dom[this.tooltipType] = this.tooltip;
12110             }
12111         }
12112         if(this.arrowTooltip){
12113             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
12114         }
12115         if(this.hidden){
12116             this.hide();
12117         }
12118         if(this.disabled){
12119             this.disable();
12120         }
12121         if(this.pressed){
12122             this.el.addClass("x-btn-pressed");
12123         }
12124         if(Roo.isIE && !Roo.isIE7){
12125             this.autoWidth.defer(1, this);
12126         }else{
12127             this.autoWidth();
12128         }
12129         if(this.menu){
12130             this.menu.on("show", this.onMenuShow, this);
12131             this.menu.on("hide", this.onMenuHide, this);
12132         }
12133         this.fireEvent('render', this);
12134     },
12135
12136     // private
12137     autoWidth : function(){
12138         if(this.el){
12139             var tbl = this.el.child("table:first");
12140             var tbl2 = this.el.child("table:last");
12141             this.el.setWidth("auto");
12142             tbl.setWidth("auto");
12143             if(Roo.isIE7 && Roo.isStrict){
12144                 var ib = this.el.child('button:first');
12145                 if(ib && ib.getWidth() > 20){
12146                     ib.clip();
12147                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12148                 }
12149             }
12150             if(this.minWidth){
12151                 if(this.hidden){
12152                     this.el.beginMeasure();
12153                 }
12154                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12155                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12156                 }
12157                 if(this.hidden){
12158                     this.el.endMeasure();
12159                 }
12160             }
12161             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12162         } 
12163     },
12164     /**
12165      * Sets this button's click handler
12166      * @param {Function} handler The function to call when the button is clicked
12167      * @param {Object} scope (optional) Scope for the function passed above
12168      */
12169     setHandler : function(handler, scope){
12170         this.handler = handler;
12171         this.scope = scope;  
12172     },
12173     
12174     /**
12175      * Sets this button's arrow click handler
12176      * @param {Function} handler The function to call when the arrow is clicked
12177      * @param {Object} scope (optional) Scope for the function passed above
12178      */
12179     setArrowHandler : function(handler, scope){
12180         this.arrowHandler = handler;
12181         this.scope = scope;  
12182     },
12183     
12184     /**
12185      * Focus the button
12186      */
12187     focus : function(){
12188         if(this.el){
12189             this.el.child("button:first").focus();
12190         }
12191     },
12192
12193     // private
12194     onClick : function(e){
12195         e.preventDefault();
12196         if(!this.disabled){
12197             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12198                 if(this.menu && !this.menu.isVisible()){
12199                     this.menu.show(this.el, this.menuAlign);
12200                 }
12201                 this.fireEvent("arrowclick", this, e);
12202                 if(this.arrowHandler){
12203                     this.arrowHandler.call(this.scope || this, this, e);
12204                 }
12205             }else{
12206                 this.fireEvent("click", this, e);
12207                 if(this.handler){
12208                     this.handler.call(this.scope || this, this, e);
12209                 }
12210             }
12211         }
12212     },
12213     // private
12214     onMouseDown : function(e){
12215         if(!this.disabled){
12216             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12217         }
12218     },
12219     // private
12220     onMouseUp : function(e){
12221         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12222     }   
12223 });
12224
12225
12226 // backwards compat
12227 Roo.MenuButton = Roo.SplitButton;/*
12228  * Based on:
12229  * Ext JS Library 1.1.1
12230  * Copyright(c) 2006-2007, Ext JS, LLC.
12231  *
12232  * Originally Released Under LGPL - original licence link has changed is not relivant.
12233  *
12234  * Fork - LGPL
12235  * <script type="text/javascript">
12236  */
12237
12238 /**
12239  * @class Roo.Toolbar
12240  * Basic Toolbar class.
12241  * @constructor
12242  * Creates a new Toolbar
12243  * @param {Object} config The config object
12244  */ 
12245 Roo.Toolbar = function(container, buttons, config)
12246 {
12247     /// old consturctor format still supported..
12248     if(container instanceof Array){ // omit the container for later rendering
12249         buttons = container;
12250         config = buttons;
12251         container = null;
12252     }
12253     if (typeof(container) == 'object' && container.xtype) {
12254         config = container;
12255         container = config.container;
12256         buttons = config.buttons; // not really - use items!!
12257     }
12258     var xitems = [];
12259     if (config && config.items) {
12260         xitems = config.items;
12261         delete config.items;
12262     }
12263     Roo.apply(this, config);
12264     this.buttons = buttons;
12265     
12266     if(container){
12267         this.render(container);
12268     }
12269     Roo.each(xitems, function(b) {
12270         this.add(b);
12271     }, this);
12272     
12273 };
12274
12275 Roo.Toolbar.prototype = {
12276     /**
12277      * @cfg {Roo.data.Store} items
12278      * array of button configs or elements to add
12279      */
12280     
12281     /**
12282      * @cfg {String/HTMLElement/Element} container
12283      * The id or element that will contain the toolbar
12284      */
12285     // private
12286     render : function(ct){
12287         this.el = Roo.get(ct);
12288         if(this.cls){
12289             this.el.addClass(this.cls);
12290         }
12291         // using a table allows for vertical alignment
12292         // 100% width is needed by Safari...
12293         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12294         this.tr = this.el.child("tr", true);
12295         var autoId = 0;
12296         this.items = new Roo.util.MixedCollection(false, function(o){
12297             return o.id || ("item" + (++autoId));
12298         });
12299         if(this.buttons){
12300             this.add.apply(this, this.buttons);
12301             delete this.buttons;
12302         }
12303     },
12304
12305     /**
12306      * Adds element(s) to the toolbar -- this function takes a variable number of 
12307      * arguments of mixed type and adds them to the toolbar.
12308      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12309      * <ul>
12310      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12311      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12312      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12313      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12314      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12315      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12316      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12317      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12318      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12319      * </ul>
12320      * @param {Mixed} arg2
12321      * @param {Mixed} etc.
12322      */
12323     add : function(){
12324         var a = arguments, l = a.length;
12325         for(var i = 0; i < l; i++){
12326             this._add(a[i]);
12327         }
12328     },
12329     // private..
12330     _add : function(el) {
12331         
12332         if (el.xtype) {
12333             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12334         }
12335         
12336         if (el.applyTo){ // some kind of form field
12337             return this.addField(el);
12338         } 
12339         if (el.render){ // some kind of Toolbar.Item
12340             return this.addItem(el);
12341         }
12342         if (typeof el == "string"){ // string
12343             if(el == "separator" || el == "-"){
12344                 return this.addSeparator();
12345             }
12346             if (el == " "){
12347                 return this.addSpacer();
12348             }
12349             if(el == "->"){
12350                 return this.addFill();
12351             }
12352             return this.addText(el);
12353             
12354         }
12355         if(el.tagName){ // element
12356             return this.addElement(el);
12357         }
12358         if(typeof el == "object"){ // must be button config?
12359             return this.addButton(el);
12360         }
12361         // and now what?!?!
12362         return false;
12363         
12364     },
12365     
12366     /**
12367      * Add an Xtype element
12368      * @param {Object} xtype Xtype Object
12369      * @return {Object} created Object
12370      */
12371     addxtype : function(e){
12372         return this.add(e);  
12373     },
12374     
12375     /**
12376      * Returns the Element for this toolbar.
12377      * @return {Roo.Element}
12378      */
12379     getEl : function(){
12380         return this.el;  
12381     },
12382     
12383     /**
12384      * Adds a separator
12385      * @return {Roo.Toolbar.Item} The separator item
12386      */
12387     addSeparator : function(){
12388         return this.addItem(new Roo.Toolbar.Separator());
12389     },
12390
12391     /**
12392      * Adds a spacer element
12393      * @return {Roo.Toolbar.Spacer} The spacer item
12394      */
12395     addSpacer : function(){
12396         return this.addItem(new Roo.Toolbar.Spacer());
12397     },
12398
12399     /**
12400      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12401      * @return {Roo.Toolbar.Fill} The fill item
12402      */
12403     addFill : function(){
12404         return this.addItem(new Roo.Toolbar.Fill());
12405     },
12406
12407     /**
12408      * Adds any standard HTML element to the toolbar
12409      * @param {String/HTMLElement/Element} el The element or id of the element to add
12410      * @return {Roo.Toolbar.Item} The element's item
12411      */
12412     addElement : function(el){
12413         return this.addItem(new Roo.Toolbar.Item(el));
12414     },
12415     /**
12416      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12417      * @type Roo.util.MixedCollection  
12418      */
12419     items : false,
12420      
12421     /**
12422      * Adds any Toolbar.Item or subclass
12423      * @param {Roo.Toolbar.Item} item
12424      * @return {Roo.Toolbar.Item} The item
12425      */
12426     addItem : function(item){
12427         var td = this.nextBlock();
12428         item.render(td);
12429         this.items.add(item);
12430         return item;
12431     },
12432     
12433     /**
12434      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12435      * @param {Object/Array} config A button config or array of configs
12436      * @return {Roo.Toolbar.Button/Array}
12437      */
12438     addButton : function(config){
12439         if(config instanceof Array){
12440             var buttons = [];
12441             for(var i = 0, len = config.length; i < len; i++) {
12442                 buttons.push(this.addButton(config[i]));
12443             }
12444             return buttons;
12445         }
12446         var b = config;
12447         if(!(config instanceof Roo.Toolbar.Button)){
12448             b = config.split ?
12449                 new Roo.Toolbar.SplitButton(config) :
12450                 new Roo.Toolbar.Button(config);
12451         }
12452         var td = this.nextBlock();
12453         b.render(td);
12454         this.items.add(b);
12455         return b;
12456     },
12457     
12458     /**
12459      * Adds text to the toolbar
12460      * @param {String} text The text to add
12461      * @return {Roo.Toolbar.Item} The element's item
12462      */
12463     addText : function(text){
12464         return this.addItem(new Roo.Toolbar.TextItem(text));
12465     },
12466     
12467     /**
12468      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12469      * @param {Number} index The index where the item is to be inserted
12470      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12471      * @return {Roo.Toolbar.Button/Item}
12472      */
12473     insertButton : function(index, item){
12474         if(item instanceof Array){
12475             var buttons = [];
12476             for(var i = 0, len = item.length; i < len; i++) {
12477                buttons.push(this.insertButton(index + i, item[i]));
12478             }
12479             return buttons;
12480         }
12481         if (!(item instanceof Roo.Toolbar.Button)){
12482            item = new Roo.Toolbar.Button(item);
12483         }
12484         var td = document.createElement("td");
12485         this.tr.insertBefore(td, this.tr.childNodes[index]);
12486         item.render(td);
12487         this.items.insert(index, item);
12488         return item;
12489     },
12490     
12491     /**
12492      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12493      * @param {Object} config
12494      * @return {Roo.Toolbar.Item} The element's item
12495      */
12496     addDom : function(config, returnEl){
12497         var td = this.nextBlock();
12498         Roo.DomHelper.overwrite(td, config);
12499         var ti = new Roo.Toolbar.Item(td.firstChild);
12500         ti.render(td);
12501         this.items.add(ti);
12502         return ti;
12503     },
12504
12505     /**
12506      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12507      * @type Roo.util.MixedCollection  
12508      */
12509     fields : false,
12510     
12511     /**
12512      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
12513      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
12514      * @param {Roo.form.Field} field
12515      * @return {Roo.ToolbarItem}
12516      */
12517      
12518       
12519     addField : function(field) {
12520         if (!this.fields) {
12521             var autoId = 0;
12522             this.fields = new Roo.util.MixedCollection(false, function(o){
12523                 return o.id || ("item" + (++autoId));
12524             });
12525
12526         }
12527         
12528         var td = this.nextBlock();
12529         field.render(td);
12530         var ti = new Roo.Toolbar.Item(td.firstChild);
12531         ti.render(td);
12532         this.items.add(ti);
12533         this.fields.add(field);
12534         return ti;
12535     },
12536     /**
12537      * Hide the toolbar
12538      * @method hide
12539      */
12540      
12541       
12542     hide : function()
12543     {
12544         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12545         this.el.child('div').hide();
12546     },
12547     /**
12548      * Show the toolbar
12549      * @method show
12550      */
12551     show : function()
12552     {
12553         this.el.child('div').show();
12554     },
12555       
12556     // private
12557     nextBlock : function(){
12558         var td = document.createElement("td");
12559         this.tr.appendChild(td);
12560         return td;
12561     },
12562
12563     // private
12564     destroy : function(){
12565         if(this.items){ // rendered?
12566             Roo.destroy.apply(Roo, this.items.items);
12567         }
12568         if(this.fields){ // rendered?
12569             Roo.destroy.apply(Roo, this.fields.items);
12570         }
12571         Roo.Element.uncache(this.el, this.tr);
12572     }
12573 };
12574
12575 /**
12576  * @class Roo.Toolbar.Item
12577  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12578  * @constructor
12579  * Creates a new Item
12580  * @param {HTMLElement} el 
12581  */
12582 Roo.Toolbar.Item = function(el){
12583     this.el = Roo.getDom(el);
12584     this.id = Roo.id(this.el);
12585     this.hidden = false;
12586 };
12587
12588 Roo.Toolbar.Item.prototype = {
12589     
12590     /**
12591      * Get this item's HTML Element
12592      * @return {HTMLElement}
12593      */
12594     getEl : function(){
12595        return this.el;  
12596     },
12597
12598     // private
12599     render : function(td){
12600         this.td = td;
12601         td.appendChild(this.el);
12602     },
12603     
12604     /**
12605      * Removes and destroys this item.
12606      */
12607     destroy : function(){
12608         this.td.parentNode.removeChild(this.td);
12609     },
12610     
12611     /**
12612      * Shows this item.
12613      */
12614     show: function(){
12615         this.hidden = false;
12616         this.td.style.display = "";
12617     },
12618     
12619     /**
12620      * Hides this item.
12621      */
12622     hide: function(){
12623         this.hidden = true;
12624         this.td.style.display = "none";
12625     },
12626     
12627     /**
12628      * Convenience function for boolean show/hide.
12629      * @param {Boolean} visible true to show/false to hide
12630      */
12631     setVisible: function(visible){
12632         if(visible) {
12633             this.show();
12634         }else{
12635             this.hide();
12636         }
12637     },
12638     
12639     /**
12640      * Try to focus this item.
12641      */
12642     focus : function(){
12643         Roo.fly(this.el).focus();
12644     },
12645     
12646     /**
12647      * Disables this item.
12648      */
12649     disable : function(){
12650         Roo.fly(this.td).addClass("x-item-disabled");
12651         this.disabled = true;
12652         this.el.disabled = true;
12653     },
12654     
12655     /**
12656      * Enables this item.
12657      */
12658     enable : function(){
12659         Roo.fly(this.td).removeClass("x-item-disabled");
12660         this.disabled = false;
12661         this.el.disabled = false;
12662     }
12663 };
12664
12665
12666 /**
12667  * @class Roo.Toolbar.Separator
12668  * @extends Roo.Toolbar.Item
12669  * A simple toolbar separator class
12670  * @constructor
12671  * Creates a new Separator
12672  */
12673 Roo.Toolbar.Separator = function(){
12674     var s = document.createElement("span");
12675     s.className = "ytb-sep";
12676     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12677 };
12678 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12679     enable:Roo.emptyFn,
12680     disable:Roo.emptyFn,
12681     focus:Roo.emptyFn
12682 });
12683
12684 /**
12685  * @class Roo.Toolbar.Spacer
12686  * @extends Roo.Toolbar.Item
12687  * A simple element that adds extra horizontal space to a toolbar.
12688  * @constructor
12689  * Creates a new Spacer
12690  */
12691 Roo.Toolbar.Spacer = function(){
12692     var s = document.createElement("div");
12693     s.className = "ytb-spacer";
12694     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12695 };
12696 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12697     enable:Roo.emptyFn,
12698     disable:Roo.emptyFn,
12699     focus:Roo.emptyFn
12700 });
12701
12702 /**
12703  * @class Roo.Toolbar.Fill
12704  * @extends Roo.Toolbar.Spacer
12705  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12706  * @constructor
12707  * Creates a new Spacer
12708  */
12709 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12710     // private
12711     render : function(td){
12712         td.style.width = '100%';
12713         Roo.Toolbar.Fill.superclass.render.call(this, td);
12714     }
12715 });
12716
12717 /**
12718  * @class Roo.Toolbar.TextItem
12719  * @extends Roo.Toolbar.Item
12720  * A simple class that renders text directly into a toolbar.
12721  * @constructor
12722  * Creates a new TextItem
12723  * @param {String} text
12724  */
12725 Roo.Toolbar.TextItem = function(text){
12726     if (typeof(text) == 'object') {
12727         text = text.text;
12728     }
12729     var s = document.createElement("span");
12730     s.className = "ytb-text";
12731     s.innerHTML = text;
12732     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12733 };
12734 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12735     enable:Roo.emptyFn,
12736     disable:Roo.emptyFn,
12737     focus:Roo.emptyFn
12738 });
12739
12740 /**
12741  * @class Roo.Toolbar.Button
12742  * @extends Roo.Button
12743  * A button that renders into a toolbar.
12744  * @constructor
12745  * Creates a new Button
12746  * @param {Object} config A standard {@link Roo.Button} config object
12747  */
12748 Roo.Toolbar.Button = function(config){
12749     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12750 };
12751 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12752     render : function(td){
12753         this.td = td;
12754         Roo.Toolbar.Button.superclass.render.call(this, td);
12755     },
12756     
12757     /**
12758      * Removes and destroys this button
12759      */
12760     destroy : function(){
12761         Roo.Toolbar.Button.superclass.destroy.call(this);
12762         this.td.parentNode.removeChild(this.td);
12763     },
12764     
12765     /**
12766      * Shows this button
12767      */
12768     show: function(){
12769         this.hidden = false;
12770         this.td.style.display = "";
12771     },
12772     
12773     /**
12774      * Hides this button
12775      */
12776     hide: function(){
12777         this.hidden = true;
12778         this.td.style.display = "none";
12779     },
12780
12781     /**
12782      * Disables this item
12783      */
12784     disable : function(){
12785         Roo.fly(this.td).addClass("x-item-disabled");
12786         this.disabled = true;
12787     },
12788
12789     /**
12790      * Enables this item
12791      */
12792     enable : function(){
12793         Roo.fly(this.td).removeClass("x-item-disabled");
12794         this.disabled = false;
12795     }
12796 });
12797 // backwards compat
12798 Roo.ToolbarButton = Roo.Toolbar.Button;
12799
12800 /**
12801  * @class Roo.Toolbar.SplitButton
12802  * @extends Roo.SplitButton
12803  * A menu button that renders into a toolbar.
12804  * @constructor
12805  * Creates a new SplitButton
12806  * @param {Object} config A standard {@link Roo.SplitButton} config object
12807  */
12808 Roo.Toolbar.SplitButton = function(config){
12809     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12810 };
12811 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12812     render : function(td){
12813         this.td = td;
12814         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12815     },
12816     
12817     /**
12818      * Removes and destroys this button
12819      */
12820     destroy : function(){
12821         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12822         this.td.parentNode.removeChild(this.td);
12823     },
12824     
12825     /**
12826      * Shows this button
12827      */
12828     show: function(){
12829         this.hidden = false;
12830         this.td.style.display = "";
12831     },
12832     
12833     /**
12834      * Hides this button
12835      */
12836     hide: function(){
12837         this.hidden = true;
12838         this.td.style.display = "none";
12839     }
12840 });
12841
12842 // backwards compat
12843 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12844  * Based on:
12845  * Ext JS Library 1.1.1
12846  * Copyright(c) 2006-2007, Ext JS, LLC.
12847  *
12848  * Originally Released Under LGPL - original licence link has changed is not relivant.
12849  *
12850  * Fork - LGPL
12851  * <script type="text/javascript">
12852  */
12853  
12854 /**
12855  * @class Roo.PagingToolbar
12856  * @extends Roo.Toolbar
12857  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12858  * @constructor
12859  * Create a new PagingToolbar
12860  * @param {Object} config The config object
12861  */
12862 Roo.PagingToolbar = function(el, ds, config)
12863 {
12864     // old args format still supported... - xtype is prefered..
12865     if (typeof(el) == 'object' && el.xtype) {
12866         // created from xtype...
12867         config = el;
12868         ds = el.dataSource;
12869         el = config.container;
12870     }
12871     var items = [];
12872     if (config.items) {
12873         items = config.items;
12874         config.items = [];
12875     }
12876     
12877     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12878     this.ds = ds;
12879     this.cursor = 0;
12880     this.renderButtons(this.el);
12881     this.bind(ds);
12882     
12883     // supprot items array.
12884    
12885     Roo.each(items, function(e) {
12886         this.add(Roo.factory(e));
12887     },this);
12888     
12889 };
12890
12891 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12892     /**
12893      * @cfg {Roo.data.Store} dataSource
12894      * The underlying data store providing the paged data
12895      */
12896     /**
12897      * @cfg {String/HTMLElement/Element} container
12898      * container The id or element that will contain the toolbar
12899      */
12900     /**
12901      * @cfg {Boolean} displayInfo
12902      * True to display the displayMsg (defaults to false)
12903      */
12904     /**
12905      * @cfg {Number} pageSize
12906      * The number of records to display per page (defaults to 20)
12907      */
12908     pageSize: 20,
12909     /**
12910      * @cfg {String} displayMsg
12911      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12912      */
12913     displayMsg : 'Displaying {0} - {1} of {2}',
12914     /**
12915      * @cfg {String} emptyMsg
12916      * The message to display when no records are found (defaults to "No data to display")
12917      */
12918     emptyMsg : 'No data to display',
12919     /**
12920      * Customizable piece of the default paging text (defaults to "Page")
12921      * @type String
12922      */
12923     beforePageText : "Page",
12924     /**
12925      * Customizable piece of the default paging text (defaults to "of %0")
12926      * @type String
12927      */
12928     afterPageText : "of {0}",
12929     /**
12930      * Customizable piece of the default paging text (defaults to "First Page")
12931      * @type String
12932      */
12933     firstText : "First Page",
12934     /**
12935      * Customizable piece of the default paging text (defaults to "Previous Page")
12936      * @type String
12937      */
12938     prevText : "Previous Page",
12939     /**
12940      * Customizable piece of the default paging text (defaults to "Next Page")
12941      * @type String
12942      */
12943     nextText : "Next Page",
12944     /**
12945      * Customizable piece of the default paging text (defaults to "Last Page")
12946      * @type String
12947      */
12948     lastText : "Last Page",
12949     /**
12950      * Customizable piece of the default paging text (defaults to "Refresh")
12951      * @type String
12952      */
12953     refreshText : "Refresh",
12954
12955     // private
12956     renderButtons : function(el){
12957         Roo.PagingToolbar.superclass.render.call(this, el);
12958         this.first = this.addButton({
12959             tooltip: this.firstText,
12960             cls: "x-btn-icon x-grid-page-first",
12961             disabled: true,
12962             handler: this.onClick.createDelegate(this, ["first"])
12963         });
12964         this.prev = this.addButton({
12965             tooltip: this.prevText,
12966             cls: "x-btn-icon x-grid-page-prev",
12967             disabled: true,
12968             handler: this.onClick.createDelegate(this, ["prev"])
12969         });
12970         //this.addSeparator();
12971         this.add(this.beforePageText);
12972         this.field = Roo.get(this.addDom({
12973            tag: "input",
12974            type: "text",
12975            size: "3",
12976            value: "1",
12977            cls: "x-grid-page-number"
12978         }).el);
12979         this.field.on("keydown", this.onPagingKeydown, this);
12980         this.field.on("focus", function(){this.dom.select();});
12981         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
12982         this.field.setHeight(18);
12983         //this.addSeparator();
12984         this.next = this.addButton({
12985             tooltip: this.nextText,
12986             cls: "x-btn-icon x-grid-page-next",
12987             disabled: true,
12988             handler: this.onClick.createDelegate(this, ["next"])
12989         });
12990         this.last = this.addButton({
12991             tooltip: this.lastText,
12992             cls: "x-btn-icon x-grid-page-last",
12993             disabled: true,
12994             handler: this.onClick.createDelegate(this, ["last"])
12995         });
12996         //this.addSeparator();
12997         this.loading = this.addButton({
12998             tooltip: this.refreshText,
12999             cls: "x-btn-icon x-grid-loading",
13000             handler: this.onClick.createDelegate(this, ["refresh"])
13001         });
13002
13003         if(this.displayInfo){
13004             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
13005         }
13006     },
13007
13008     // private
13009     updateInfo : function(){
13010         if(this.displayEl){
13011             var count = this.ds.getCount();
13012             var msg = count == 0 ?
13013                 this.emptyMsg :
13014                 String.format(
13015                     this.displayMsg,
13016                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
13017                 );
13018             this.displayEl.update(msg);
13019         }
13020     },
13021
13022     // private
13023     onLoad : function(ds, r, o){
13024        this.cursor = o.params ? o.params.start : 0;
13025        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
13026
13027        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
13028        this.field.dom.value = ap;
13029        this.first.setDisabled(ap == 1);
13030        this.prev.setDisabled(ap == 1);
13031        this.next.setDisabled(ap == ps);
13032        this.last.setDisabled(ap == ps);
13033        this.loading.enable();
13034        this.updateInfo();
13035     },
13036
13037     // private
13038     getPageData : function(){
13039         var total = this.ds.getTotalCount();
13040         return {
13041             total : total,
13042             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
13043             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
13044         };
13045     },
13046
13047     // private
13048     onLoadError : function(){
13049         this.loading.enable();
13050     },
13051
13052     // private
13053     onPagingKeydown : function(e){
13054         var k = e.getKey();
13055         var d = this.getPageData();
13056         if(k == e.RETURN){
13057             var v = this.field.dom.value, pageNum;
13058             if(!v || isNaN(pageNum = parseInt(v, 10))){
13059                 this.field.dom.value = d.activePage;
13060                 return;
13061             }
13062             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
13063             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13064             e.stopEvent();
13065         }
13066         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))
13067         {
13068           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
13069           this.field.dom.value = pageNum;
13070           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13071           e.stopEvent();
13072         }
13073         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13074         {
13075           var v = this.field.dom.value, pageNum; 
13076           var increment = (e.shiftKey) ? 10 : 1;
13077           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13078             increment *= -1;
13079           if(!v || isNaN(pageNum = parseInt(v, 10))) {
13080             this.field.dom.value = d.activePage;
13081             return;
13082           }
13083           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13084           {
13085             this.field.dom.value = parseInt(v, 10) + increment;
13086             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13087             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13088           }
13089           e.stopEvent();
13090         }
13091     },
13092
13093     // private
13094     beforeLoad : function(){
13095         if(this.loading){
13096             this.loading.disable();
13097         }
13098     },
13099
13100     // private
13101     onClick : function(which){
13102         var ds = this.ds;
13103         switch(which){
13104             case "first":
13105                 ds.load({params:{start: 0, limit: this.pageSize}});
13106             break;
13107             case "prev":
13108                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13109             break;
13110             case "next":
13111                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13112             break;
13113             case "last":
13114                 var total = ds.getTotalCount();
13115                 var extra = total % this.pageSize;
13116                 var lastStart = extra ? (total - extra) : total-this.pageSize;
13117                 ds.load({params:{start: lastStart, limit: this.pageSize}});
13118             break;
13119             case "refresh":
13120                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13121             break;
13122         }
13123     },
13124
13125     /**
13126      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13127      * @param {Roo.data.Store} store The data store to unbind
13128      */
13129     unbind : function(ds){
13130         ds.un("beforeload", this.beforeLoad, this);
13131         ds.un("load", this.onLoad, this);
13132         ds.un("loadexception", this.onLoadError, this);
13133         ds.un("remove", this.updateInfo, this);
13134         ds.un("add", this.updateInfo, this);
13135         this.ds = undefined;
13136     },
13137
13138     /**
13139      * Binds the paging toolbar to the specified {@link Roo.data.Store}
13140      * @param {Roo.data.Store} store The data store to bind
13141      */
13142     bind : function(ds){
13143         ds.on("beforeload", this.beforeLoad, this);
13144         ds.on("load", this.onLoad, this);
13145         ds.on("loadexception", this.onLoadError, this);
13146         ds.on("remove", this.updateInfo, this);
13147         ds.on("add", this.updateInfo, this);
13148         this.ds = ds;
13149     }
13150 });/*
13151  * Based on:
13152  * Ext JS Library 1.1.1
13153  * Copyright(c) 2006-2007, Ext JS, LLC.
13154  *
13155  * Originally Released Under LGPL - original licence link has changed is not relivant.
13156  *
13157  * Fork - LGPL
13158  * <script type="text/javascript">
13159  */
13160
13161 /**
13162  * @class Roo.Resizable
13163  * @extends Roo.util.Observable
13164  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13165  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13166  * 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
13167  * the element will be wrapped for you automatically.</p>
13168  * <p>Here is the list of valid resize handles:</p>
13169  * <pre>
13170 Value   Description
13171 ------  -------------------
13172  'n'     north
13173  's'     south
13174  'e'     east
13175  'w'     west
13176  'nw'    northwest
13177  'sw'    southwest
13178  'se'    southeast
13179  'ne'    northeast
13180  'hd'    horizontal drag
13181  'all'   all
13182 </pre>
13183  * <p>Here's an example showing the creation of a typical Resizable:</p>
13184  * <pre><code>
13185 var resizer = new Roo.Resizable("element-id", {
13186     handles: 'all',
13187     minWidth: 200,
13188     minHeight: 100,
13189     maxWidth: 500,
13190     maxHeight: 400,
13191     pinned: true
13192 });
13193 resizer.on("resize", myHandler);
13194 </code></pre>
13195  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13196  * resizer.east.setDisplayed(false);</p>
13197  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13198  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13199  * resize operation's new size (defaults to [0, 0])
13200  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13201  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13202  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13203  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13204  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13205  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13206  * @cfg {Number} width The width of the element in pixels (defaults to null)
13207  * @cfg {Number} height The height of the element in pixels (defaults to null)
13208  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13209  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13210  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13211  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13212  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13213  * in favor of the handles config option (defaults to false)
13214  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13215  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13216  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13217  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13218  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13219  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13220  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13221  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13222  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13223  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13224  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13225  * @constructor
13226  * Create a new resizable component
13227  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13228  * @param {Object} config configuration options
13229   */
13230 Roo.Resizable = function(el, config)
13231 {
13232     this.el = Roo.get(el);
13233
13234     if(config && config.wrap){
13235         config.resizeChild = this.el;
13236         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13237         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13238         this.el.setStyle("overflow", "hidden");
13239         this.el.setPositioning(config.resizeChild.getPositioning());
13240         config.resizeChild.clearPositioning();
13241         if(!config.width || !config.height){
13242             var csize = config.resizeChild.getSize();
13243             this.el.setSize(csize.width, csize.height);
13244         }
13245         if(config.pinned && !config.adjustments){
13246             config.adjustments = "auto";
13247         }
13248     }
13249
13250     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13251     this.proxy.unselectable();
13252     this.proxy.enableDisplayMode('block');
13253
13254     Roo.apply(this, config);
13255
13256     if(this.pinned){
13257         this.disableTrackOver = true;
13258         this.el.addClass("x-resizable-pinned");
13259     }
13260     // if the element isn't positioned, make it relative
13261     var position = this.el.getStyle("position");
13262     if(position != "absolute" && position != "fixed"){
13263         this.el.setStyle("position", "relative");
13264     }
13265     if(!this.handles){ // no handles passed, must be legacy style
13266         this.handles = 's,e,se';
13267         if(this.multiDirectional){
13268             this.handles += ',n,w';
13269         }
13270     }
13271     if(this.handles == "all"){
13272         this.handles = "n s e w ne nw se sw";
13273     }
13274     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13275     var ps = Roo.Resizable.positions;
13276     for(var i = 0, len = hs.length; i < len; i++){
13277         if(hs[i] && ps[hs[i]]){
13278             var pos = ps[hs[i]];
13279             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13280         }
13281     }
13282     // legacy
13283     this.corner = this.southeast;
13284     
13285     // updateBox = the box can move..
13286     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13287         this.updateBox = true;
13288     }
13289
13290     this.activeHandle = null;
13291
13292     if(this.resizeChild){
13293         if(typeof this.resizeChild == "boolean"){
13294             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13295         }else{
13296             this.resizeChild = Roo.get(this.resizeChild, true);
13297         }
13298     }
13299     
13300     if(this.adjustments == "auto"){
13301         var rc = this.resizeChild;
13302         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13303         if(rc && (hw || hn)){
13304             rc.position("relative");
13305             rc.setLeft(hw ? hw.el.getWidth() : 0);
13306             rc.setTop(hn ? hn.el.getHeight() : 0);
13307         }
13308         this.adjustments = [
13309             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13310             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13311         ];
13312     }
13313
13314     if(this.draggable){
13315         this.dd = this.dynamic ?
13316             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13317         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13318     }
13319
13320     // public events
13321     this.addEvents({
13322         /**
13323          * @event beforeresize
13324          * Fired before resize is allowed. Set enabled to false to cancel resize.
13325          * @param {Roo.Resizable} this
13326          * @param {Roo.EventObject} e The mousedown event
13327          */
13328         "beforeresize" : true,
13329         /**
13330          * @event resize
13331          * Fired after a resize.
13332          * @param {Roo.Resizable} this
13333          * @param {Number} width The new width
13334          * @param {Number} height The new height
13335          * @param {Roo.EventObject} e The mouseup event
13336          */
13337         "resize" : true
13338     });
13339
13340     if(this.width !== null && this.height !== null){
13341         this.resizeTo(this.width, this.height);
13342     }else{
13343         this.updateChildSize();
13344     }
13345     if(Roo.isIE){
13346         this.el.dom.style.zoom = 1;
13347     }
13348     Roo.Resizable.superclass.constructor.call(this);
13349 };
13350
13351 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13352         resizeChild : false,
13353         adjustments : [0, 0],
13354         minWidth : 5,
13355         minHeight : 5,
13356         maxWidth : 10000,
13357         maxHeight : 10000,
13358         enabled : true,
13359         animate : false,
13360         duration : .35,
13361         dynamic : false,
13362         handles : false,
13363         multiDirectional : false,
13364         disableTrackOver : false,
13365         easing : 'easeOutStrong',
13366         widthIncrement : 0,
13367         heightIncrement : 0,
13368         pinned : false,
13369         width : null,
13370         height : null,
13371         preserveRatio : false,
13372         transparent: false,
13373         minX: 0,
13374         minY: 0,
13375         draggable: false,
13376
13377         /**
13378          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13379          */
13380         constrainTo: undefined,
13381         /**
13382          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13383          */
13384         resizeRegion: undefined,
13385
13386
13387     /**
13388      * Perform a manual resize
13389      * @param {Number} width
13390      * @param {Number} height
13391      */
13392     resizeTo : function(width, height){
13393         this.el.setSize(width, height);
13394         this.updateChildSize();
13395         this.fireEvent("resize", this, width, height, null);
13396     },
13397
13398     // private
13399     startSizing : function(e, handle){
13400         this.fireEvent("beforeresize", this, e);
13401         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13402
13403             if(!this.overlay){
13404                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13405                 this.overlay.unselectable();
13406                 this.overlay.enableDisplayMode("block");
13407                 this.overlay.on("mousemove", this.onMouseMove, this);
13408                 this.overlay.on("mouseup", this.onMouseUp, this);
13409             }
13410             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13411
13412             this.resizing = true;
13413             this.startBox = this.el.getBox();
13414             this.startPoint = e.getXY();
13415             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13416                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13417
13418             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13419             this.overlay.show();
13420
13421             if(this.constrainTo) {
13422                 var ct = Roo.get(this.constrainTo);
13423                 this.resizeRegion = ct.getRegion().adjust(
13424                     ct.getFrameWidth('t'),
13425                     ct.getFrameWidth('l'),
13426                     -ct.getFrameWidth('b'),
13427                     -ct.getFrameWidth('r')
13428                 );
13429             }
13430
13431             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13432             this.proxy.show();
13433             this.proxy.setBox(this.startBox);
13434             if(!this.dynamic){
13435                 this.proxy.setStyle('visibility', 'visible');
13436             }
13437         }
13438     },
13439
13440     // private
13441     onMouseDown : function(handle, e){
13442         if(this.enabled){
13443             e.stopEvent();
13444             this.activeHandle = handle;
13445             this.startSizing(e, handle);
13446         }
13447     },
13448
13449     // private
13450     onMouseUp : function(e){
13451         var size = this.resizeElement();
13452         this.resizing = false;
13453         this.handleOut();
13454         this.overlay.hide();
13455         this.proxy.hide();
13456         this.fireEvent("resize", this, size.width, size.height, e);
13457     },
13458
13459     // private
13460     updateChildSize : function(){
13461         if(this.resizeChild){
13462             var el = this.el;
13463             var child = this.resizeChild;
13464             var adj = this.adjustments;
13465             if(el.dom.offsetWidth){
13466                 var b = el.getSize(true);
13467                 child.setSize(b.width+adj[0], b.height+adj[1]);
13468             }
13469             // Second call here for IE
13470             // The first call enables instant resizing and
13471             // the second call corrects scroll bars if they
13472             // exist
13473             if(Roo.isIE){
13474                 setTimeout(function(){
13475                     if(el.dom.offsetWidth){
13476                         var b = el.getSize(true);
13477                         child.setSize(b.width+adj[0], b.height+adj[1]);
13478                     }
13479                 }, 10);
13480             }
13481         }
13482     },
13483
13484     // private
13485     snap : function(value, inc, min){
13486         if(!inc || !value) return value;
13487         var newValue = value;
13488         var m = value % inc;
13489         if(m > 0){
13490             if(m > (inc/2)){
13491                 newValue = value + (inc-m);
13492             }else{
13493                 newValue = value - m;
13494             }
13495         }
13496         return Math.max(min, newValue);
13497     },
13498
13499     // private
13500     resizeElement : function(){
13501         var box = this.proxy.getBox();
13502         if(this.updateBox){
13503             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13504         }else{
13505             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13506         }
13507         this.updateChildSize();
13508         if(!this.dynamic){
13509             this.proxy.hide();
13510         }
13511         return box;
13512     },
13513
13514     // private
13515     constrain : function(v, diff, m, mx){
13516         if(v - diff < m){
13517             diff = v - m;
13518         }else if(v - diff > mx){
13519             diff = mx - v;
13520         }
13521         return diff;
13522     },
13523
13524     // private
13525     onMouseMove : function(e){
13526         if(this.enabled){
13527             try{// try catch so if something goes wrong the user doesn't get hung
13528
13529             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13530                 return;
13531             }
13532
13533             //var curXY = this.startPoint;
13534             var curSize = this.curSize || this.startBox;
13535             var x = this.startBox.x, y = this.startBox.y;
13536             var ox = x, oy = y;
13537             var w = curSize.width, h = curSize.height;
13538             var ow = w, oh = h;
13539             var mw = this.minWidth, mh = this.minHeight;
13540             var mxw = this.maxWidth, mxh = this.maxHeight;
13541             var wi = this.widthIncrement;
13542             var hi = this.heightIncrement;
13543
13544             var eventXY = e.getXY();
13545             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13546             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13547
13548             var pos = this.activeHandle.position;
13549
13550             switch(pos){
13551                 case "east":
13552                     w += diffX;
13553                     w = Math.min(Math.max(mw, w), mxw);
13554                     break;
13555              
13556                 case "south":
13557                     h += diffY;
13558                     h = Math.min(Math.max(mh, h), mxh);
13559                     break;
13560                 case "southeast":
13561                     w += diffX;
13562                     h += diffY;
13563                     w = Math.min(Math.max(mw, w), mxw);
13564                     h = Math.min(Math.max(mh, h), mxh);
13565                     break;
13566                 case "north":
13567                     diffY = this.constrain(h, diffY, mh, mxh);
13568                     y += diffY;
13569                     h -= diffY;
13570                     break;
13571                 case "hdrag":
13572                     
13573                     if (wi) {
13574                         var adiffX = Math.abs(diffX);
13575                         var sub = (adiffX % wi); // how much 
13576                         if (sub > (wi/2)) { // far enough to snap
13577                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13578                         } else {
13579                             // remove difference.. 
13580                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13581                         }
13582                     }
13583                     x += diffX;
13584                     x = Math.max(this.minX, x);
13585                     break;
13586                 case "west":
13587                     diffX = this.constrain(w, diffX, mw, mxw);
13588                     x += diffX;
13589                     w -= diffX;
13590                     break;
13591                 case "northeast":
13592                     w += diffX;
13593                     w = Math.min(Math.max(mw, w), mxw);
13594                     diffY = this.constrain(h, diffY, mh, mxh);
13595                     y += diffY;
13596                     h -= diffY;
13597                     break;
13598                 case "northwest":
13599                     diffX = this.constrain(w, diffX, mw, mxw);
13600                     diffY = this.constrain(h, diffY, mh, mxh);
13601                     y += diffY;
13602                     h -= diffY;
13603                     x += diffX;
13604                     w -= diffX;
13605                     break;
13606                case "southwest":
13607                     diffX = this.constrain(w, diffX, mw, mxw);
13608                     h += diffY;
13609                     h = Math.min(Math.max(mh, h), mxh);
13610                     x += diffX;
13611                     w -= diffX;
13612                     break;
13613             }
13614
13615             var sw = this.snap(w, wi, mw);
13616             var sh = this.snap(h, hi, mh);
13617             if(sw != w || sh != h){
13618                 switch(pos){
13619                     case "northeast":
13620                         y -= sh - h;
13621                     break;
13622                     case "north":
13623                         y -= sh - h;
13624                         break;
13625                     case "southwest":
13626                         x -= sw - w;
13627                     break;
13628                     case "west":
13629                         x -= sw - w;
13630                         break;
13631                     case "northwest":
13632                         x -= sw - w;
13633                         y -= sh - h;
13634                     break;
13635                 }
13636                 w = sw;
13637                 h = sh;
13638             }
13639
13640             if(this.preserveRatio){
13641                 switch(pos){
13642                     case "southeast":
13643                     case "east":
13644                         h = oh * (w/ow);
13645                         h = Math.min(Math.max(mh, h), mxh);
13646                         w = ow * (h/oh);
13647                        break;
13648                     case "south":
13649                         w = ow * (h/oh);
13650                         w = Math.min(Math.max(mw, w), mxw);
13651                         h = oh * (w/ow);
13652                         break;
13653                     case "northeast":
13654                         w = ow * (h/oh);
13655                         w = Math.min(Math.max(mw, w), mxw);
13656                         h = oh * (w/ow);
13657                     break;
13658                     case "north":
13659                         var tw = w;
13660                         w = ow * (h/oh);
13661                         w = Math.min(Math.max(mw, w), mxw);
13662                         h = oh * (w/ow);
13663                         x += (tw - w) / 2;
13664                         break;
13665                     case "southwest":
13666                         h = oh * (w/ow);
13667                         h = Math.min(Math.max(mh, h), mxh);
13668                         var tw = w;
13669                         w = ow * (h/oh);
13670                         x += tw - w;
13671                         break;
13672                     case "west":
13673                         var th = h;
13674                         h = oh * (w/ow);
13675                         h = Math.min(Math.max(mh, h), mxh);
13676                         y += (th - h) / 2;
13677                         var tw = w;
13678                         w = ow * (h/oh);
13679                         x += tw - w;
13680                        break;
13681                     case "northwest":
13682                         var tw = w;
13683                         var th = h;
13684                         h = oh * (w/ow);
13685                         h = Math.min(Math.max(mh, h), mxh);
13686                         w = ow * (h/oh);
13687                         y += th - h;
13688                         x += tw - w;
13689                        break;
13690
13691                 }
13692             }
13693             if (pos == 'hdrag') {
13694                 w = ow;
13695             }
13696             this.proxy.setBounds(x, y, w, h);
13697             if(this.dynamic){
13698                 this.resizeElement();
13699             }
13700             }catch(e){}
13701         }
13702     },
13703
13704     // private
13705     handleOver : function(){
13706         if(this.enabled){
13707             this.el.addClass("x-resizable-over");
13708         }
13709     },
13710
13711     // private
13712     handleOut : function(){
13713         if(!this.resizing){
13714             this.el.removeClass("x-resizable-over");
13715         }
13716     },
13717
13718     /**
13719      * Returns the element this component is bound to.
13720      * @return {Roo.Element}
13721      */
13722     getEl : function(){
13723         return this.el;
13724     },
13725
13726     /**
13727      * Returns the resizeChild element (or null).
13728      * @return {Roo.Element}
13729      */
13730     getResizeChild : function(){
13731         return this.resizeChild;
13732     },
13733
13734     /**
13735      * Destroys this resizable. If the element was wrapped and
13736      * removeEl is not true then the element remains.
13737      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13738      */
13739     destroy : function(removeEl){
13740         this.proxy.remove();
13741         if(this.overlay){
13742             this.overlay.removeAllListeners();
13743             this.overlay.remove();
13744         }
13745         var ps = Roo.Resizable.positions;
13746         for(var k in ps){
13747             if(typeof ps[k] != "function" && this[ps[k]]){
13748                 var h = this[ps[k]];
13749                 h.el.removeAllListeners();
13750                 h.el.remove();
13751             }
13752         }
13753         if(removeEl){
13754             this.el.update("");
13755             this.el.remove();
13756         }
13757     }
13758 });
13759
13760 // private
13761 // hash to map config positions to true positions
13762 Roo.Resizable.positions = {
13763     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13764     hd: "hdrag"
13765 };
13766
13767 // private
13768 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13769     if(!this.tpl){
13770         // only initialize the template if resizable is used
13771         var tpl = Roo.DomHelper.createTemplate(
13772             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13773         );
13774         tpl.compile();
13775         Roo.Resizable.Handle.prototype.tpl = tpl;
13776     }
13777     this.position = pos;
13778     this.rz = rz;
13779     // show north drag fro topdra
13780     var handlepos = pos == 'hdrag' ? 'north' : pos;
13781     
13782     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13783     if (pos == 'hdrag') {
13784         this.el.setStyle('cursor', 'pointer');
13785     }
13786     this.el.unselectable();
13787     if(transparent){
13788         this.el.setOpacity(0);
13789     }
13790     this.el.on("mousedown", this.onMouseDown, this);
13791     if(!disableTrackOver){
13792         this.el.on("mouseover", this.onMouseOver, this);
13793         this.el.on("mouseout", this.onMouseOut, this);
13794     }
13795 };
13796
13797 // private
13798 Roo.Resizable.Handle.prototype = {
13799     afterResize : function(rz){
13800         // do nothing
13801     },
13802     // private
13803     onMouseDown : function(e){
13804         this.rz.onMouseDown(this, e);
13805     },
13806     // private
13807     onMouseOver : function(e){
13808         this.rz.handleOver(this, e);
13809     },
13810     // private
13811     onMouseOut : function(e){
13812         this.rz.handleOut(this, e);
13813     }
13814 };/*
13815  * Based on:
13816  * Ext JS Library 1.1.1
13817  * Copyright(c) 2006-2007, Ext JS, LLC.
13818  *
13819  * Originally Released Under LGPL - original licence link has changed is not relivant.
13820  *
13821  * Fork - LGPL
13822  * <script type="text/javascript">
13823  */
13824
13825 /**
13826  * @class Roo.Editor
13827  * @extends Roo.Component
13828  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13829  * @constructor
13830  * Create a new Editor
13831  * @param {Roo.form.Field} field The Field object (or descendant)
13832  * @param {Object} config The config object
13833  */
13834 Roo.Editor = function(field, config){
13835     Roo.Editor.superclass.constructor.call(this, config);
13836     this.field = field;
13837     this.addEvents({
13838         /**
13839              * @event beforestartedit
13840              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13841              * false from the handler of this event.
13842              * @param {Editor} this
13843              * @param {Roo.Element} boundEl The underlying element bound to this editor
13844              * @param {Mixed} value The field value being set
13845              */
13846         "beforestartedit" : true,
13847         /**
13848              * @event startedit
13849              * Fires when this editor is displayed
13850              * @param {Roo.Element} boundEl The underlying element bound to this editor
13851              * @param {Mixed} value The starting field value
13852              */
13853         "startedit" : true,
13854         /**
13855              * @event beforecomplete
13856              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13857              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13858              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13859              * event will not fire since no edit actually occurred.
13860              * @param {Editor} this
13861              * @param {Mixed} value The current field value
13862              * @param {Mixed} startValue The original field value
13863              */
13864         "beforecomplete" : true,
13865         /**
13866              * @event complete
13867              * Fires after editing is complete and any changed value has been written to the underlying field.
13868              * @param {Editor} this
13869              * @param {Mixed} value The current field value
13870              * @param {Mixed} startValue The original field value
13871              */
13872         "complete" : true,
13873         /**
13874          * @event specialkey
13875          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13876          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13877          * @param {Roo.form.Field} this
13878          * @param {Roo.EventObject} e The event object
13879          */
13880         "specialkey" : true
13881     });
13882 };
13883
13884 Roo.extend(Roo.Editor, Roo.Component, {
13885     /**
13886      * @cfg {Boolean/String} autosize
13887      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13888      * or "height" to adopt the height only (defaults to false)
13889      */
13890     /**
13891      * @cfg {Boolean} revertInvalid
13892      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13893      * validation fails (defaults to true)
13894      */
13895     /**
13896      * @cfg {Boolean} ignoreNoChange
13897      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13898      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
13899      * will never be ignored.
13900      */
13901     /**
13902      * @cfg {Boolean} hideEl
13903      * False to keep the bound element visible while the editor is displayed (defaults to true)
13904      */
13905     /**
13906      * @cfg {Mixed} value
13907      * The data value of the underlying field (defaults to "")
13908      */
13909     value : "",
13910     /**
13911      * @cfg {String} alignment
13912      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13913      */
13914     alignment: "c-c?",
13915     /**
13916      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13917      * for bottom-right shadow (defaults to "frame")
13918      */
13919     shadow : "frame",
13920     /**
13921      * @cfg {Boolean} constrain True to constrain the editor to the viewport
13922      */
13923     constrain : false,
13924     /**
13925      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13926      */
13927     completeOnEnter : false,
13928     /**
13929      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
13930      */
13931     cancelOnEsc : false,
13932     /**
13933      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
13934      */
13935     updateEl : false,
13936
13937     // private
13938     onRender : function(ct, position){
13939         this.el = new Roo.Layer({
13940             shadow: this.shadow,
13941             cls: "x-editor",
13942             parentEl : ct,
13943             shim : this.shim,
13944             shadowOffset:4,
13945             id: this.id,
13946             constrain: this.constrain
13947         });
13948         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
13949         if(this.field.msgTarget != 'title'){
13950             this.field.msgTarget = 'qtip';
13951         }
13952         this.field.render(this.el);
13953         if(Roo.isGecko){
13954             this.field.el.dom.setAttribute('autocomplete', 'off');
13955         }
13956         this.field.on("specialkey", this.onSpecialKey, this);
13957         if(this.swallowKeys){
13958             this.field.el.swallowEvent(['keydown','keypress']);
13959         }
13960         this.field.show();
13961         this.field.on("blur", this.onBlur, this);
13962         if(this.field.grow){
13963             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
13964         }
13965     },
13966
13967     onSpecialKey : function(field, e)
13968     {
13969         //Roo.log('editor onSpecialKey');
13970         if(this.completeOnEnter && e.getKey() == e.ENTER){
13971             e.stopEvent();
13972             this.completeEdit();
13973             return;
13974         }
13975         // do not fire special key otherwise it might hide close the editor...
13976         if(e.getKey() == e.ENTER){    
13977             return;
13978         }
13979         if(this.cancelOnEsc && e.getKey() == e.ESC){
13980             this.cancelEdit();
13981             return;
13982         } 
13983         this.fireEvent('specialkey', field, e);
13984     
13985     },
13986
13987     /**
13988      * Starts the editing process and shows the editor.
13989      * @param {String/HTMLElement/Element} el The element to edit
13990      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
13991       * to the innerHTML of el.
13992      */
13993     startEdit : function(el, value){
13994         if(this.editing){
13995             this.completeEdit();
13996         }
13997         this.boundEl = Roo.get(el);
13998         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
13999         if(!this.rendered){
14000             this.render(this.parentEl || document.body);
14001         }
14002         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
14003             return;
14004         }
14005         this.startValue = v;
14006         this.field.setValue(v);
14007         if(this.autoSize){
14008             var sz = this.boundEl.getSize();
14009             switch(this.autoSize){
14010                 case "width":
14011                 this.setSize(sz.width,  "");
14012                 break;
14013                 case "height":
14014                 this.setSize("",  sz.height);
14015                 break;
14016                 default:
14017                 this.setSize(sz.width,  sz.height);
14018             }
14019         }
14020         this.el.alignTo(this.boundEl, this.alignment);
14021         this.editing = true;
14022         if(Roo.QuickTips){
14023             Roo.QuickTips.disable();
14024         }
14025         this.show();
14026     },
14027
14028     /**
14029      * Sets the height and width of this editor.
14030      * @param {Number} width The new width
14031      * @param {Number} height The new height
14032      */
14033     setSize : function(w, h){
14034         this.field.setSize(w, h);
14035         if(this.el){
14036             this.el.sync();
14037         }
14038     },
14039
14040     /**
14041      * Realigns the editor to the bound field based on the current alignment config value.
14042      */
14043     realign : function(){
14044         this.el.alignTo(this.boundEl, this.alignment);
14045     },
14046
14047     /**
14048      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
14049      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
14050      */
14051     completeEdit : function(remainVisible){
14052         if(!this.editing){
14053             return;
14054         }
14055         var v = this.getValue();
14056         if(this.revertInvalid !== false && !this.field.isValid()){
14057             v = this.startValue;
14058             this.cancelEdit(true);
14059         }
14060         if(String(v) === String(this.startValue) && this.ignoreNoChange){
14061             this.editing = false;
14062             this.hide();
14063             return;
14064         }
14065         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
14066             this.editing = false;
14067             if(this.updateEl && this.boundEl){
14068                 this.boundEl.update(v);
14069             }
14070             if(remainVisible !== true){
14071                 this.hide();
14072             }
14073             this.fireEvent("complete", this, v, this.startValue);
14074         }
14075     },
14076
14077     // private
14078     onShow : function(){
14079         this.el.show();
14080         if(this.hideEl !== false){
14081             this.boundEl.hide();
14082         }
14083         this.field.show();
14084         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14085             this.fixIEFocus = true;
14086             this.deferredFocus.defer(50, this);
14087         }else{
14088             this.field.focus();
14089         }
14090         this.fireEvent("startedit", this.boundEl, this.startValue);
14091     },
14092
14093     deferredFocus : function(){
14094         if(this.editing){
14095             this.field.focus();
14096         }
14097     },
14098
14099     /**
14100      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
14101      * reverted to the original starting value.
14102      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14103      * cancel (defaults to false)
14104      */
14105     cancelEdit : function(remainVisible){
14106         if(this.editing){
14107             this.setValue(this.startValue);
14108             if(remainVisible !== true){
14109                 this.hide();
14110             }
14111         }
14112     },
14113
14114     // private
14115     onBlur : function(){
14116         if(this.allowBlur !== true && this.editing){
14117             this.completeEdit();
14118         }
14119     },
14120
14121     // private
14122     onHide : function(){
14123         if(this.editing){
14124             this.completeEdit();
14125             return;
14126         }
14127         this.field.blur();
14128         if(this.field.collapse){
14129             this.field.collapse();
14130         }
14131         this.el.hide();
14132         if(this.hideEl !== false){
14133             this.boundEl.show();
14134         }
14135         if(Roo.QuickTips){
14136             Roo.QuickTips.enable();
14137         }
14138     },
14139
14140     /**
14141      * Sets the data value of the editor
14142      * @param {Mixed} value Any valid value supported by the underlying field
14143      */
14144     setValue : function(v){
14145         this.field.setValue(v);
14146     },
14147
14148     /**
14149      * Gets the data value of the editor
14150      * @return {Mixed} The data value
14151      */
14152     getValue : function(){
14153         return this.field.getValue();
14154     }
14155 });/*
14156  * Based on:
14157  * Ext JS Library 1.1.1
14158  * Copyright(c) 2006-2007, Ext JS, LLC.
14159  *
14160  * Originally Released Under LGPL - original licence link has changed is not relivant.
14161  *
14162  * Fork - LGPL
14163  * <script type="text/javascript">
14164  */
14165  
14166 /**
14167  * @class Roo.BasicDialog
14168  * @extends Roo.util.Observable
14169  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
14170  * <pre><code>
14171 var dlg = new Roo.BasicDialog("my-dlg", {
14172     height: 200,
14173     width: 300,
14174     minHeight: 100,
14175     minWidth: 150,
14176     modal: true,
14177     proxyDrag: true,
14178     shadow: true
14179 });
14180 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14181 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14182 dlg.addButton('Cancel', dlg.hide, dlg);
14183 dlg.show();
14184 </code></pre>
14185   <b>A Dialog should always be a direct child of the body element.</b>
14186  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14187  * @cfg {String} title Default text to display in the title bar (defaults to null)
14188  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14189  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14190  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14191  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14192  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14193  * (defaults to null with no animation)
14194  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14195  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14196  * property for valid values (defaults to 'all')
14197  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14198  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14199  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14200  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14201  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14202  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14203  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14204  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14205  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14206  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14207  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14208  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14209  * draggable = true (defaults to false)
14210  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14211  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14212  * shadow (defaults to false)
14213  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14214  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14215  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14216  * @cfg {Array} buttons Array of buttons
14217  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14218  * @constructor
14219  * Create a new BasicDialog.
14220  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14221  * @param {Object} config Configuration options
14222  */
14223 Roo.BasicDialog = function(el, config){
14224     this.el = Roo.get(el);
14225     var dh = Roo.DomHelper;
14226     if(!this.el && config && config.autoCreate){
14227         if(typeof config.autoCreate == "object"){
14228             if(!config.autoCreate.id){
14229                 config.autoCreate.id = el;
14230             }
14231             this.el = dh.append(document.body,
14232                         config.autoCreate, true);
14233         }else{
14234             this.el = dh.append(document.body,
14235                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14236         }
14237     }
14238     el = this.el;
14239     el.setDisplayed(true);
14240     el.hide = this.hideAction;
14241     this.id = el.id;
14242     el.addClass("x-dlg");
14243
14244     Roo.apply(this, config);
14245
14246     this.proxy = el.createProxy("x-dlg-proxy");
14247     this.proxy.hide = this.hideAction;
14248     this.proxy.setOpacity(.5);
14249     this.proxy.hide();
14250
14251     if(config.width){
14252         el.setWidth(config.width);
14253     }
14254     if(config.height){
14255         el.setHeight(config.height);
14256     }
14257     this.size = el.getSize();
14258     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14259         this.xy = [config.x,config.y];
14260     }else{
14261         this.xy = el.getCenterXY(true);
14262     }
14263     /** The header element @type Roo.Element */
14264     this.header = el.child("> .x-dlg-hd");
14265     /** The body element @type Roo.Element */
14266     this.body = el.child("> .x-dlg-bd");
14267     /** The footer element @type Roo.Element */
14268     this.footer = el.child("> .x-dlg-ft");
14269
14270     if(!this.header){
14271         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14272     }
14273     if(!this.body){
14274         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14275     }
14276
14277     this.header.unselectable();
14278     if(this.title){
14279         this.header.update(this.title);
14280     }
14281     // this element allows the dialog to be focused for keyboard event
14282     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14283     this.focusEl.swallowEvent("click", true);
14284
14285     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14286
14287     // wrap the body and footer for special rendering
14288     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14289     if(this.footer){
14290         this.bwrap.dom.appendChild(this.footer.dom);
14291     }
14292
14293     this.bg = this.el.createChild({
14294         tag: "div", cls:"x-dlg-bg",
14295         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14296     });
14297     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14298
14299
14300     if(this.autoScroll !== false && !this.autoTabs){
14301         this.body.setStyle("overflow", "auto");
14302     }
14303
14304     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14305
14306     if(this.closable !== false){
14307         this.el.addClass("x-dlg-closable");
14308         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14309         this.close.on("click", this.closeClick, this);
14310         this.close.addClassOnOver("x-dlg-close-over");
14311     }
14312     if(this.collapsible !== false){
14313         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14314         this.collapseBtn.on("click", this.collapseClick, this);
14315         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14316         this.header.on("dblclick", this.collapseClick, this);
14317     }
14318     if(this.resizable !== false){
14319         this.el.addClass("x-dlg-resizable");
14320         this.resizer = new Roo.Resizable(el, {
14321             minWidth: this.minWidth || 80,
14322             minHeight:this.minHeight || 80,
14323             handles: this.resizeHandles || "all",
14324             pinned: true
14325         });
14326         this.resizer.on("beforeresize", this.beforeResize, this);
14327         this.resizer.on("resize", this.onResize, this);
14328     }
14329     if(this.draggable !== false){
14330         el.addClass("x-dlg-draggable");
14331         if (!this.proxyDrag) {
14332             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14333         }
14334         else {
14335             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14336         }
14337         dd.setHandleElId(this.header.id);
14338         dd.endDrag = this.endMove.createDelegate(this);
14339         dd.startDrag = this.startMove.createDelegate(this);
14340         dd.onDrag = this.onDrag.createDelegate(this);
14341         dd.scroll = false;
14342         this.dd = dd;
14343     }
14344     if(this.modal){
14345         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14346         this.mask.enableDisplayMode("block");
14347         this.mask.hide();
14348         this.el.addClass("x-dlg-modal");
14349     }
14350     if(this.shadow){
14351         this.shadow = new Roo.Shadow({
14352             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14353             offset : this.shadowOffset
14354         });
14355     }else{
14356         this.shadowOffset = 0;
14357     }
14358     if(Roo.useShims && this.shim !== false){
14359         this.shim = this.el.createShim();
14360         this.shim.hide = this.hideAction;
14361         this.shim.hide();
14362     }else{
14363         this.shim = false;
14364     }
14365     if(this.autoTabs){
14366         this.initTabs();
14367     }
14368     if (this.buttons) { 
14369         var bts= this.buttons;
14370         this.buttons = [];
14371         Roo.each(bts, function(b) {
14372             this.addButton(b);
14373         }, this);
14374     }
14375     
14376     
14377     this.addEvents({
14378         /**
14379          * @event keydown
14380          * Fires when a key is pressed
14381          * @param {Roo.BasicDialog} this
14382          * @param {Roo.EventObject} e
14383          */
14384         "keydown" : true,
14385         /**
14386          * @event move
14387          * Fires when this dialog is moved by the user.
14388          * @param {Roo.BasicDialog} this
14389          * @param {Number} x The new page X
14390          * @param {Number} y The new page Y
14391          */
14392         "move" : true,
14393         /**
14394          * @event resize
14395          * Fires when this dialog is resized by the user.
14396          * @param {Roo.BasicDialog} this
14397          * @param {Number} width The new width
14398          * @param {Number} height The new height
14399          */
14400         "resize" : true,
14401         /**
14402          * @event beforehide
14403          * Fires before this dialog is hidden.
14404          * @param {Roo.BasicDialog} this
14405          */
14406         "beforehide" : true,
14407         /**
14408          * @event hide
14409          * Fires when this dialog is hidden.
14410          * @param {Roo.BasicDialog} this
14411          */
14412         "hide" : true,
14413         /**
14414          * @event beforeshow
14415          * Fires before this dialog is shown.
14416          * @param {Roo.BasicDialog} this
14417          */
14418         "beforeshow" : true,
14419         /**
14420          * @event show
14421          * Fires when this dialog is shown.
14422          * @param {Roo.BasicDialog} this
14423          */
14424         "show" : true
14425     });
14426     el.on("keydown", this.onKeyDown, this);
14427     el.on("mousedown", this.toFront, this);
14428     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14429     this.el.hide();
14430     Roo.DialogManager.register(this);
14431     Roo.BasicDialog.superclass.constructor.call(this);
14432 };
14433
14434 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14435     shadowOffset: Roo.isIE ? 6 : 5,
14436     minHeight: 80,
14437     minWidth: 200,
14438     minButtonWidth: 75,
14439     defaultButton: null,
14440     buttonAlign: "right",
14441     tabTag: 'div',
14442     firstShow: true,
14443
14444     /**
14445      * Sets the dialog title text
14446      * @param {String} text The title text to display
14447      * @return {Roo.BasicDialog} this
14448      */
14449     setTitle : function(text){
14450         this.header.update(text);
14451         return this;
14452     },
14453
14454     // private
14455     closeClick : function(){
14456         this.hide();
14457     },
14458
14459     // private
14460     collapseClick : function(){
14461         this[this.collapsed ? "expand" : "collapse"]();
14462     },
14463
14464     /**
14465      * Collapses the dialog to its minimized state (only the title bar is visible).
14466      * Equivalent to the user clicking the collapse dialog button.
14467      */
14468     collapse : function(){
14469         if(!this.collapsed){
14470             this.collapsed = true;
14471             this.el.addClass("x-dlg-collapsed");
14472             this.restoreHeight = this.el.getHeight();
14473             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14474         }
14475     },
14476
14477     /**
14478      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14479      * clicking the expand dialog button.
14480      */
14481     expand : function(){
14482         if(this.collapsed){
14483             this.collapsed = false;
14484             this.el.removeClass("x-dlg-collapsed");
14485             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14486         }
14487     },
14488
14489     /**
14490      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14491      * @return {Roo.TabPanel} The tabs component
14492      */
14493     initTabs : function(){
14494         var tabs = this.getTabs();
14495         while(tabs.getTab(0)){
14496             tabs.removeTab(0);
14497         }
14498         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14499             var dom = el.dom;
14500             tabs.addTab(Roo.id(dom), dom.title);
14501             dom.title = "";
14502         });
14503         tabs.activate(0);
14504         return tabs;
14505     },
14506
14507     // private
14508     beforeResize : function(){
14509         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14510     },
14511
14512     // private
14513     onResize : function(){
14514         this.refreshSize();
14515         this.syncBodyHeight();
14516         this.adjustAssets();
14517         this.focus();
14518         this.fireEvent("resize", this, this.size.width, this.size.height);
14519     },
14520
14521     // private
14522     onKeyDown : function(e){
14523         if(this.isVisible()){
14524             this.fireEvent("keydown", this, e);
14525         }
14526     },
14527
14528     /**
14529      * Resizes the dialog.
14530      * @param {Number} width
14531      * @param {Number} height
14532      * @return {Roo.BasicDialog} this
14533      */
14534     resizeTo : function(width, height){
14535         this.el.setSize(width, height);
14536         this.size = {width: width, height: height};
14537         this.syncBodyHeight();
14538         if(this.fixedcenter){
14539             this.center();
14540         }
14541         if(this.isVisible()){
14542             this.constrainXY();
14543             this.adjustAssets();
14544         }
14545         this.fireEvent("resize", this, width, height);
14546         return this;
14547     },
14548
14549
14550     /**
14551      * Resizes the dialog to fit the specified content size.
14552      * @param {Number} width
14553      * @param {Number} height
14554      * @return {Roo.BasicDialog} this
14555      */
14556     setContentSize : function(w, h){
14557         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14558         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14559         //if(!this.el.isBorderBox()){
14560             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14561             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14562         //}
14563         if(this.tabs){
14564             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14565             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14566         }
14567         this.resizeTo(w, h);
14568         return this;
14569     },
14570
14571     /**
14572      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14573      * executed in response to a particular key being pressed while the dialog is active.
14574      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14575      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14576      * @param {Function} fn The function to call
14577      * @param {Object} scope (optional) The scope of the function
14578      * @return {Roo.BasicDialog} this
14579      */
14580     addKeyListener : function(key, fn, scope){
14581         var keyCode, shift, ctrl, alt;
14582         if(typeof key == "object" && !(key instanceof Array)){
14583             keyCode = key["key"];
14584             shift = key["shift"];
14585             ctrl = key["ctrl"];
14586             alt = key["alt"];
14587         }else{
14588             keyCode = key;
14589         }
14590         var handler = function(dlg, e){
14591             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14592                 var k = e.getKey();
14593                 if(keyCode instanceof Array){
14594                     for(var i = 0, len = keyCode.length; i < len; i++){
14595                         if(keyCode[i] == k){
14596                           fn.call(scope || window, dlg, k, e);
14597                           return;
14598                         }
14599                     }
14600                 }else{
14601                     if(k == keyCode){
14602                         fn.call(scope || window, dlg, k, e);
14603                     }
14604                 }
14605             }
14606         };
14607         this.on("keydown", handler);
14608         return this;
14609     },
14610
14611     /**
14612      * Returns the TabPanel component (creates it if it doesn't exist).
14613      * Note: If you wish to simply check for the existence of tabs without creating them,
14614      * check for a null 'tabs' property.
14615      * @return {Roo.TabPanel} The tabs component
14616      */
14617     getTabs : function(){
14618         if(!this.tabs){
14619             this.el.addClass("x-dlg-auto-tabs");
14620             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14621             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14622         }
14623         return this.tabs;
14624     },
14625
14626     /**
14627      * Adds a button to the footer section of the dialog.
14628      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14629      * object or a valid Roo.DomHelper element config
14630      * @param {Function} handler The function called when the button is clicked
14631      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14632      * @return {Roo.Button} The new button
14633      */
14634     addButton : function(config, handler, scope){
14635         var dh = Roo.DomHelper;
14636         if(!this.footer){
14637             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14638         }
14639         if(!this.btnContainer){
14640             var tb = this.footer.createChild({
14641
14642                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14643                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14644             }, null, true);
14645             this.btnContainer = tb.firstChild.firstChild.firstChild;
14646         }
14647         var bconfig = {
14648             handler: handler,
14649             scope: scope,
14650             minWidth: this.minButtonWidth,
14651             hideParent:true
14652         };
14653         if(typeof config == "string"){
14654             bconfig.text = config;
14655         }else{
14656             if(config.tag){
14657                 bconfig.dhconfig = config;
14658             }else{
14659                 Roo.apply(bconfig, config);
14660             }
14661         }
14662         var fc = false;
14663         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14664             bconfig.position = Math.max(0, bconfig.position);
14665             fc = this.btnContainer.childNodes[bconfig.position];
14666         }
14667          
14668         var btn = new Roo.Button(
14669             fc ? 
14670                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14671                 : this.btnContainer.appendChild(document.createElement("td")),
14672             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14673             bconfig
14674         );
14675         this.syncBodyHeight();
14676         if(!this.buttons){
14677             /**
14678              * Array of all the buttons that have been added to this dialog via addButton
14679              * @type Array
14680              */
14681             this.buttons = [];
14682         }
14683         this.buttons.push(btn);
14684         return btn;
14685     },
14686
14687     /**
14688      * Sets the default button to be focused when the dialog is displayed.
14689      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14690      * @return {Roo.BasicDialog} this
14691      */
14692     setDefaultButton : function(btn){
14693         this.defaultButton = btn;
14694         return this;
14695     },
14696
14697     // private
14698     getHeaderFooterHeight : function(safe){
14699         var height = 0;
14700         if(this.header){
14701            height += this.header.getHeight();
14702         }
14703         if(this.footer){
14704            var fm = this.footer.getMargins();
14705             height += (this.footer.getHeight()+fm.top+fm.bottom);
14706         }
14707         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14708         height += this.centerBg.getPadding("tb");
14709         return height;
14710     },
14711
14712     // private
14713     syncBodyHeight : function(){
14714         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
14715         var height = this.size.height - this.getHeaderFooterHeight(false);
14716         bd.setHeight(height-bd.getMargins("tb"));
14717         var hh = this.header.getHeight();
14718         var h = this.size.height-hh;
14719         cb.setHeight(h);
14720         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14721         bw.setHeight(h-cb.getPadding("tb"));
14722         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14723         bd.setWidth(bw.getWidth(true));
14724         if(this.tabs){
14725             this.tabs.syncHeight();
14726             if(Roo.isIE){
14727                 this.tabs.el.repaint();
14728             }
14729         }
14730     },
14731
14732     /**
14733      * Restores the previous state of the dialog if Roo.state is configured.
14734      * @return {Roo.BasicDialog} this
14735      */
14736     restoreState : function(){
14737         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14738         if(box && box.width){
14739             this.xy = [box.x, box.y];
14740             this.resizeTo(box.width, box.height);
14741         }
14742         return this;
14743     },
14744
14745     // private
14746     beforeShow : function(){
14747         this.expand();
14748         if(this.fixedcenter){
14749             this.xy = this.el.getCenterXY(true);
14750         }
14751         if(this.modal){
14752             Roo.get(document.body).addClass("x-body-masked");
14753             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14754             this.mask.show();
14755         }
14756         this.constrainXY();
14757     },
14758
14759     // private
14760     animShow : function(){
14761         var b = Roo.get(this.animateTarget).getBox();
14762         this.proxy.setSize(b.width, b.height);
14763         this.proxy.setLocation(b.x, b.y);
14764         this.proxy.show();
14765         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14766                     true, .35, this.showEl.createDelegate(this));
14767     },
14768
14769     /**
14770      * Shows the dialog.
14771      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14772      * @return {Roo.BasicDialog} this
14773      */
14774     show : function(animateTarget){
14775         if (this.fireEvent("beforeshow", this) === false){
14776             return;
14777         }
14778         if(this.syncHeightBeforeShow){
14779             this.syncBodyHeight();
14780         }else if(this.firstShow){
14781             this.firstShow = false;
14782             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14783         }
14784         this.animateTarget = animateTarget || this.animateTarget;
14785         if(!this.el.isVisible()){
14786             this.beforeShow();
14787             if(this.animateTarget && Roo.get(this.animateTarget)){
14788                 this.animShow();
14789             }else{
14790                 this.showEl();
14791             }
14792         }
14793         return this;
14794     },
14795
14796     // private
14797     showEl : function(){
14798         this.proxy.hide();
14799         this.el.setXY(this.xy);
14800         this.el.show();
14801         this.adjustAssets(true);
14802         this.toFront();
14803         this.focus();
14804         // IE peekaboo bug - fix found by Dave Fenwick
14805         if(Roo.isIE){
14806             this.el.repaint();
14807         }
14808         this.fireEvent("show", this);
14809     },
14810
14811     /**
14812      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14813      * dialog itself will receive focus.
14814      */
14815     focus : function(){
14816         if(this.defaultButton){
14817             this.defaultButton.focus();
14818         }else{
14819             this.focusEl.focus();
14820         }
14821     },
14822
14823     // private
14824     constrainXY : function(){
14825         if(this.constraintoviewport !== false){
14826             if(!this.viewSize){
14827                 if(this.container){
14828                     var s = this.container.getSize();
14829                     this.viewSize = [s.width, s.height];
14830                 }else{
14831                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14832                 }
14833             }
14834             var s = Roo.get(this.container||document).getScroll();
14835
14836             var x = this.xy[0], y = this.xy[1];
14837             var w = this.size.width, h = this.size.height;
14838             var vw = this.viewSize[0], vh = this.viewSize[1];
14839             // only move it if it needs it
14840             var moved = false;
14841             // first validate right/bottom
14842             if(x + w > vw+s.left){
14843                 x = vw - w;
14844                 moved = true;
14845             }
14846             if(y + h > vh+s.top){
14847                 y = vh - h;
14848                 moved = true;
14849             }
14850             // then make sure top/left isn't negative
14851             if(x < s.left){
14852                 x = s.left;
14853                 moved = true;
14854             }
14855             if(y < s.top){
14856                 y = s.top;
14857                 moved = true;
14858             }
14859             if(moved){
14860                 // cache xy
14861                 this.xy = [x, y];
14862                 if(this.isVisible()){
14863                     this.el.setLocation(x, y);
14864                     this.adjustAssets();
14865                 }
14866             }
14867         }
14868     },
14869
14870     // private
14871     onDrag : function(){
14872         if(!this.proxyDrag){
14873             this.xy = this.el.getXY();
14874             this.adjustAssets();
14875         }
14876     },
14877
14878     // private
14879     adjustAssets : function(doShow){
14880         var x = this.xy[0], y = this.xy[1];
14881         var w = this.size.width, h = this.size.height;
14882         if(doShow === true){
14883             if(this.shadow){
14884                 this.shadow.show(this.el);
14885             }
14886             if(this.shim){
14887                 this.shim.show();
14888             }
14889         }
14890         if(this.shadow && this.shadow.isVisible()){
14891             this.shadow.show(this.el);
14892         }
14893         if(this.shim && this.shim.isVisible()){
14894             this.shim.setBounds(x, y, w, h);
14895         }
14896     },
14897
14898     // private
14899     adjustViewport : function(w, h){
14900         if(!w || !h){
14901             w = Roo.lib.Dom.getViewWidth();
14902             h = Roo.lib.Dom.getViewHeight();
14903         }
14904         // cache the size
14905         this.viewSize = [w, h];
14906         if(this.modal && this.mask.isVisible()){
14907             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14908             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14909         }
14910         if(this.isVisible()){
14911             this.constrainXY();
14912         }
14913     },
14914
14915     /**
14916      * Destroys this dialog and all its supporting elements (including any tabs, shim,
14917      * shadow, proxy, mask, etc.)  Also removes all event listeners.
14918      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14919      */
14920     destroy : function(removeEl){
14921         if(this.isVisible()){
14922             this.animateTarget = null;
14923             this.hide();
14924         }
14925         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14926         if(this.tabs){
14927             this.tabs.destroy(removeEl);
14928         }
14929         Roo.destroy(
14930              this.shim,
14931              this.proxy,
14932              this.resizer,
14933              this.close,
14934              this.mask
14935         );
14936         if(this.dd){
14937             this.dd.unreg();
14938         }
14939         if(this.buttons){
14940            for(var i = 0, len = this.buttons.length; i < len; i++){
14941                this.buttons[i].destroy();
14942            }
14943         }
14944         this.el.removeAllListeners();
14945         if(removeEl === true){
14946             this.el.update("");
14947             this.el.remove();
14948         }
14949         Roo.DialogManager.unregister(this);
14950     },
14951
14952     // private
14953     startMove : function(){
14954         if(this.proxyDrag){
14955             this.proxy.show();
14956         }
14957         if(this.constraintoviewport !== false){
14958             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
14959         }
14960     },
14961
14962     // private
14963     endMove : function(){
14964         if(!this.proxyDrag){
14965             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
14966         }else{
14967             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
14968             this.proxy.hide();
14969         }
14970         this.refreshSize();
14971         this.adjustAssets();
14972         this.focus();
14973         this.fireEvent("move", this, this.xy[0], this.xy[1]);
14974     },
14975
14976     /**
14977      * Brings this dialog to the front of any other visible dialogs
14978      * @return {Roo.BasicDialog} this
14979      */
14980     toFront : function(){
14981         Roo.DialogManager.bringToFront(this);
14982         return this;
14983     },
14984
14985     /**
14986      * Sends this dialog to the back (under) of any other visible dialogs
14987      * @return {Roo.BasicDialog} this
14988      */
14989     toBack : function(){
14990         Roo.DialogManager.sendToBack(this);
14991         return this;
14992     },
14993
14994     /**
14995      * Centers this dialog in the viewport
14996      * @return {Roo.BasicDialog} this
14997      */
14998     center : function(){
14999         var xy = this.el.getCenterXY(true);
15000         this.moveTo(xy[0], xy[1]);
15001         return this;
15002     },
15003
15004     /**
15005      * Moves the dialog's top-left corner to the specified point
15006      * @param {Number} x
15007      * @param {Number} y
15008      * @return {Roo.BasicDialog} this
15009      */
15010     moveTo : function(x, y){
15011         this.xy = [x,y];
15012         if(this.isVisible()){
15013             this.el.setXY(this.xy);
15014             this.adjustAssets();
15015         }
15016         return this;
15017     },
15018
15019     /**
15020      * Aligns the dialog to the specified element
15021      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15022      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
15023      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15024      * @return {Roo.BasicDialog} this
15025      */
15026     alignTo : function(element, position, offsets){
15027         this.xy = this.el.getAlignToXY(element, position, offsets);
15028         if(this.isVisible()){
15029             this.el.setXY(this.xy);
15030             this.adjustAssets();
15031         }
15032         return this;
15033     },
15034
15035     /**
15036      * Anchors an element to another element and realigns it when the window is resized.
15037      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15038      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
15039      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15040      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
15041      * is a number, it is used as the buffer delay (defaults to 50ms).
15042      * @return {Roo.BasicDialog} this
15043      */
15044     anchorTo : function(el, alignment, offsets, monitorScroll){
15045         var action = function(){
15046             this.alignTo(el, alignment, offsets);
15047         };
15048         Roo.EventManager.onWindowResize(action, this);
15049         var tm = typeof monitorScroll;
15050         if(tm != 'undefined'){
15051             Roo.EventManager.on(window, 'scroll', action, this,
15052                 {buffer: tm == 'number' ? monitorScroll : 50});
15053         }
15054         action.call(this);
15055         return this;
15056     },
15057
15058     /**
15059      * Returns true if the dialog is visible
15060      * @return {Boolean}
15061      */
15062     isVisible : function(){
15063         return this.el.isVisible();
15064     },
15065
15066     // private
15067     animHide : function(callback){
15068         var b = Roo.get(this.animateTarget).getBox();
15069         this.proxy.show();
15070         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
15071         this.el.hide();
15072         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
15073                     this.hideEl.createDelegate(this, [callback]));
15074     },
15075
15076     /**
15077      * Hides the dialog.
15078      * @param {Function} callback (optional) Function to call when the dialog is hidden
15079      * @return {Roo.BasicDialog} this
15080      */
15081     hide : function(callback){
15082         if (this.fireEvent("beforehide", this) === false){
15083             return;
15084         }
15085         if(this.shadow){
15086             this.shadow.hide();
15087         }
15088         if(this.shim) {
15089           this.shim.hide();
15090         }
15091         // sometimes animateTarget seems to get set.. causing problems...
15092         // this just double checks..
15093         if(this.animateTarget && Roo.get(this.animateTarget)) {
15094            this.animHide(callback);
15095         }else{
15096             this.el.hide();
15097             this.hideEl(callback);
15098         }
15099         return this;
15100     },
15101
15102     // private
15103     hideEl : function(callback){
15104         this.proxy.hide();
15105         if(this.modal){
15106             this.mask.hide();
15107             Roo.get(document.body).removeClass("x-body-masked");
15108         }
15109         this.fireEvent("hide", this);
15110         if(typeof callback == "function"){
15111             callback();
15112         }
15113     },
15114
15115     // private
15116     hideAction : function(){
15117         this.setLeft("-10000px");
15118         this.setTop("-10000px");
15119         this.setStyle("visibility", "hidden");
15120     },
15121
15122     // private
15123     refreshSize : function(){
15124         this.size = this.el.getSize();
15125         this.xy = this.el.getXY();
15126         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15127     },
15128
15129     // private
15130     // z-index is managed by the DialogManager and may be overwritten at any time
15131     setZIndex : function(index){
15132         if(this.modal){
15133             this.mask.setStyle("z-index", index);
15134         }
15135         if(this.shim){
15136             this.shim.setStyle("z-index", ++index);
15137         }
15138         if(this.shadow){
15139             this.shadow.setZIndex(++index);
15140         }
15141         this.el.setStyle("z-index", ++index);
15142         if(this.proxy){
15143             this.proxy.setStyle("z-index", ++index);
15144         }
15145         if(this.resizer){
15146             this.resizer.proxy.setStyle("z-index", ++index);
15147         }
15148
15149         this.lastZIndex = index;
15150     },
15151
15152     /**
15153      * Returns the element for this dialog
15154      * @return {Roo.Element} The underlying dialog Element
15155      */
15156     getEl : function(){
15157         return this.el;
15158     }
15159 });
15160
15161 /**
15162  * @class Roo.DialogManager
15163  * Provides global access to BasicDialogs that have been created and
15164  * support for z-indexing (layering) multiple open dialogs.
15165  */
15166 Roo.DialogManager = function(){
15167     var list = {};
15168     var accessList = [];
15169     var front = null;
15170
15171     // private
15172     var sortDialogs = function(d1, d2){
15173         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15174     };
15175
15176     // private
15177     var orderDialogs = function(){
15178         accessList.sort(sortDialogs);
15179         var seed = Roo.DialogManager.zseed;
15180         for(var i = 0, len = accessList.length; i < len; i++){
15181             var dlg = accessList[i];
15182             if(dlg){
15183                 dlg.setZIndex(seed + (i*10));
15184             }
15185         }
15186     };
15187
15188     return {
15189         /**
15190          * The starting z-index for BasicDialogs (defaults to 9000)
15191          * @type Number The z-index value
15192          */
15193         zseed : 9000,
15194
15195         // private
15196         register : function(dlg){
15197             list[dlg.id] = dlg;
15198             accessList.push(dlg);
15199         },
15200
15201         // private
15202         unregister : function(dlg){
15203             delete list[dlg.id];
15204             var i=0;
15205             var len=0;
15206             if(!accessList.indexOf){
15207                 for(  i = 0, len = accessList.length; i < len; i++){
15208                     if(accessList[i] == dlg){
15209                         accessList.splice(i, 1);
15210                         return;
15211                     }
15212                 }
15213             }else{
15214                  i = accessList.indexOf(dlg);
15215                 if(i != -1){
15216                     accessList.splice(i, 1);
15217                 }
15218             }
15219         },
15220
15221         /**
15222          * Gets a registered dialog by id
15223          * @param {String/Object} id The id of the dialog or a dialog
15224          * @return {Roo.BasicDialog} this
15225          */
15226         get : function(id){
15227             return typeof id == "object" ? id : list[id];
15228         },
15229
15230         /**
15231          * Brings the specified dialog to the front
15232          * @param {String/Object} dlg The id of the dialog or a dialog
15233          * @return {Roo.BasicDialog} this
15234          */
15235         bringToFront : function(dlg){
15236             dlg = this.get(dlg);
15237             if(dlg != front){
15238                 front = dlg;
15239                 dlg._lastAccess = new Date().getTime();
15240                 orderDialogs();
15241             }
15242             return dlg;
15243         },
15244
15245         /**
15246          * Sends the specified dialog to the back
15247          * @param {String/Object} dlg The id of the dialog or a dialog
15248          * @return {Roo.BasicDialog} this
15249          */
15250         sendToBack : function(dlg){
15251             dlg = this.get(dlg);
15252             dlg._lastAccess = -(new Date().getTime());
15253             orderDialogs();
15254             return dlg;
15255         },
15256
15257         /**
15258          * Hides all dialogs
15259          */
15260         hideAll : function(){
15261             for(var id in list){
15262                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15263                     list[id].hide();
15264                 }
15265             }
15266         }
15267     };
15268 }();
15269
15270 /**
15271  * @class Roo.LayoutDialog
15272  * @extends Roo.BasicDialog
15273  * Dialog which provides adjustments for working with a layout in a Dialog.
15274  * Add your necessary layout config options to the dialog's config.<br>
15275  * Example usage (including a nested layout):
15276  * <pre><code>
15277 if(!dialog){
15278     dialog = new Roo.LayoutDialog("download-dlg", {
15279         modal: true,
15280         width:600,
15281         height:450,
15282         shadow:true,
15283         minWidth:500,
15284         minHeight:350,
15285         autoTabs:true,
15286         proxyDrag:true,
15287         // layout config merges with the dialog config
15288         center:{
15289             tabPosition: "top",
15290             alwaysShowTabs: true
15291         }
15292     });
15293     dialog.addKeyListener(27, dialog.hide, dialog);
15294     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15295     dialog.addButton("Build It!", this.getDownload, this);
15296
15297     // we can even add nested layouts
15298     var innerLayout = new Roo.BorderLayout("dl-inner", {
15299         east: {
15300             initialSize: 200,
15301             autoScroll:true,
15302             split:true
15303         },
15304         center: {
15305             autoScroll:true
15306         }
15307     });
15308     innerLayout.beginUpdate();
15309     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15310     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15311     innerLayout.endUpdate(true);
15312
15313     var layout = dialog.getLayout();
15314     layout.beginUpdate();
15315     layout.add("center", new Roo.ContentPanel("standard-panel",
15316                         {title: "Download the Source", fitToFrame:true}));
15317     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15318                {title: "Build your own roo.js"}));
15319     layout.getRegion("center").showPanel(sp);
15320     layout.endUpdate();
15321 }
15322 </code></pre>
15323     * @constructor
15324     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15325     * @param {Object} config configuration options
15326   */
15327 Roo.LayoutDialog = function(el, cfg){
15328     
15329     var config=  cfg;
15330     if (typeof(cfg) == 'undefined') {
15331         config = Roo.apply({}, el);
15332         // not sure why we use documentElement here.. - it should always be body.
15333         // IE7 borks horribly if we use documentElement.
15334         // webkit also does not like documentElement - it creates a body element...
15335         el = Roo.get( document.body || document.documentElement ).createChild();
15336         //config.autoCreate = true;
15337     }
15338     
15339     
15340     config.autoTabs = false;
15341     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15342     this.body.setStyle({overflow:"hidden", position:"relative"});
15343     this.layout = new Roo.BorderLayout(this.body.dom, config);
15344     this.layout.monitorWindowResize = false;
15345     this.el.addClass("x-dlg-auto-layout");
15346     // fix case when center region overwrites center function
15347     this.center = Roo.BasicDialog.prototype.center;
15348     this.on("show", this.layout.layout, this.layout, true);
15349     if (config.items) {
15350         var xitems = config.items;
15351         delete config.items;
15352         Roo.each(xitems, this.addxtype, this);
15353     }
15354     
15355     
15356 };
15357 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15358     /**
15359      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15360      * @deprecated
15361      */
15362     endUpdate : function(){
15363         this.layout.endUpdate();
15364     },
15365
15366     /**
15367      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15368      *  @deprecated
15369      */
15370     beginUpdate : function(){
15371         this.layout.beginUpdate();
15372     },
15373
15374     /**
15375      * Get the BorderLayout for this dialog
15376      * @return {Roo.BorderLayout}
15377      */
15378     getLayout : function(){
15379         return this.layout;
15380     },
15381
15382     showEl : function(){
15383         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15384         if(Roo.isIE7){
15385             this.layout.layout();
15386         }
15387     },
15388
15389     // private
15390     // Use the syncHeightBeforeShow config option to control this automatically
15391     syncBodyHeight : function(){
15392         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15393         if(this.layout){this.layout.layout();}
15394     },
15395     
15396       /**
15397      * Add an xtype element (actually adds to the layout.)
15398      * @return {Object} xdata xtype object data.
15399      */
15400     
15401     addxtype : function(c) {
15402         return this.layout.addxtype(c);
15403     }
15404 });/*
15405  * Based on:
15406  * Ext JS Library 1.1.1
15407  * Copyright(c) 2006-2007, Ext JS, LLC.
15408  *
15409  * Originally Released Under LGPL - original licence link has changed is not relivant.
15410  *
15411  * Fork - LGPL
15412  * <script type="text/javascript">
15413  */
15414  
15415 /**
15416  * @class Roo.MessageBox
15417  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15418  * Example usage:
15419  *<pre><code>
15420 // Basic alert:
15421 Roo.Msg.alert('Status', 'Changes saved successfully.');
15422
15423 // Prompt for user data:
15424 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15425     if (btn == 'ok'){
15426         // process text value...
15427     }
15428 });
15429
15430 // Show a dialog using config options:
15431 Roo.Msg.show({
15432    title:'Save Changes?',
15433    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15434    buttons: Roo.Msg.YESNOCANCEL,
15435    fn: processResult,
15436    animEl: 'elId'
15437 });
15438 </code></pre>
15439  * @singleton
15440  */
15441 Roo.MessageBox = function(){
15442     var dlg, opt, mask, waitTimer;
15443     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15444     var buttons, activeTextEl, bwidth;
15445
15446     // private
15447     var handleButton = function(button){
15448         dlg.hide();
15449         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15450     };
15451
15452     // private
15453     var handleHide = function(){
15454         if(opt && opt.cls){
15455             dlg.el.removeClass(opt.cls);
15456         }
15457         if(waitTimer){
15458             Roo.TaskMgr.stop(waitTimer);
15459             waitTimer = null;
15460         }
15461     };
15462
15463     // private
15464     var updateButtons = function(b){
15465         var width = 0;
15466         if(!b){
15467             buttons["ok"].hide();
15468             buttons["cancel"].hide();
15469             buttons["yes"].hide();
15470             buttons["no"].hide();
15471             dlg.footer.dom.style.display = 'none';
15472             return width;
15473         }
15474         dlg.footer.dom.style.display = '';
15475         for(var k in buttons){
15476             if(typeof buttons[k] != "function"){
15477                 if(b[k]){
15478                     buttons[k].show();
15479                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15480                     width += buttons[k].el.getWidth()+15;
15481                 }else{
15482                     buttons[k].hide();
15483                 }
15484             }
15485         }
15486         return width;
15487     };
15488
15489     // private
15490     var handleEsc = function(d, k, e){
15491         if(opt && opt.closable !== false){
15492             dlg.hide();
15493         }
15494         if(e){
15495             e.stopEvent();
15496         }
15497     };
15498
15499     return {
15500         /**
15501          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15502          * @return {Roo.BasicDialog} The BasicDialog element
15503          */
15504         getDialog : function(){
15505            if(!dlg){
15506                 dlg = new Roo.BasicDialog("x-msg-box", {
15507                     autoCreate : true,
15508                     shadow: true,
15509                     draggable: true,
15510                     resizable:false,
15511                     constraintoviewport:false,
15512                     fixedcenter:true,
15513                     collapsible : false,
15514                     shim:true,
15515                     modal: true,
15516                     width:400, height:100,
15517                     buttonAlign:"center",
15518                     closeClick : function(){
15519                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15520                             handleButton("no");
15521                         }else{
15522                             handleButton("cancel");
15523                         }
15524                     }
15525                 });
15526                 dlg.on("hide", handleHide);
15527                 mask = dlg.mask;
15528                 dlg.addKeyListener(27, handleEsc);
15529                 buttons = {};
15530                 var bt = this.buttonText;
15531                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15532                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15533                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15534                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15535                 bodyEl = dlg.body.createChild({
15536
15537                     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>'
15538                 });
15539                 msgEl = bodyEl.dom.firstChild;
15540                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15541                 textboxEl.enableDisplayMode();
15542                 textboxEl.addKeyListener([10,13], function(){
15543                     if(dlg.isVisible() && opt && opt.buttons){
15544                         if(opt.buttons.ok){
15545                             handleButton("ok");
15546                         }else if(opt.buttons.yes){
15547                             handleButton("yes");
15548                         }
15549                     }
15550                 });
15551                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15552                 textareaEl.enableDisplayMode();
15553                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15554                 progressEl.enableDisplayMode();
15555                 var pf = progressEl.dom.firstChild;
15556                 if (pf) {
15557                     pp = Roo.get(pf.firstChild);
15558                     pp.setHeight(pf.offsetHeight);
15559                 }
15560                 
15561             }
15562             return dlg;
15563         },
15564
15565         /**
15566          * Updates the message box body text
15567          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15568          * the XHTML-compliant non-breaking space character '&amp;#160;')
15569          * @return {Roo.MessageBox} This message box
15570          */
15571         updateText : function(text){
15572             if(!dlg.isVisible() && !opt.width){
15573                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15574             }
15575             msgEl.innerHTML = text || '&#160;';
15576             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
15577                         Math.max(opt.minWidth || this.minWidth, bwidth));
15578             if(opt.prompt){
15579                 activeTextEl.setWidth(w);
15580             }
15581             if(dlg.isVisible()){
15582                 dlg.fixedcenter = false;
15583             }
15584             dlg.setContentSize(w, bodyEl.getHeight());
15585             if(dlg.isVisible()){
15586                 dlg.fixedcenter = true;
15587             }
15588             return this;
15589         },
15590
15591         /**
15592          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15593          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15594          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15595          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15596          * @return {Roo.MessageBox} This message box
15597          */
15598         updateProgress : function(value, text){
15599             if(text){
15600                 this.updateText(text);
15601             }
15602             if (pp) { // weird bug on my firefox - for some reason this is not defined
15603                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15604             }
15605             return this;
15606         },        
15607
15608         /**
15609          * Returns true if the message box is currently displayed
15610          * @return {Boolean} True if the message box is visible, else false
15611          */
15612         isVisible : function(){
15613             return dlg && dlg.isVisible();  
15614         },
15615
15616         /**
15617          * Hides the message box if it is displayed
15618          */
15619         hide : function(){
15620             if(this.isVisible()){
15621                 dlg.hide();
15622             }  
15623         },
15624
15625         /**
15626          * Displays a new message box, or reinitializes an existing message box, based on the config options
15627          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15628          * The following config object properties are supported:
15629          * <pre>
15630 Property    Type             Description
15631 ----------  ---------------  ------------------------------------------------------------------------------------
15632 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15633                                    closes (defaults to undefined)
15634 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15635                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15636 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15637                                    progress and wait dialogs will ignore this property and always hide the
15638                                    close button as they can only be closed programmatically.
15639 cls               String           A custom CSS class to apply to the message box element
15640 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15641                                    displayed (defaults to 75)
15642 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15643                                    function will be btn (the name of the button that was clicked, if applicable,
15644                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15645                                    Progress and wait dialogs will ignore this option since they do not respond to
15646                                    user actions and can only be closed programmatically, so any required function
15647                                    should be called by the same code after it closes the dialog.
15648 icon              String           A CSS class that provides a background image to be used as an icon for
15649                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15650 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15651 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15652 modal             Boolean          False to allow user interaction with the page while the message box is
15653                                    displayed (defaults to true)
15654 msg               String           A string that will replace the existing message box body text (defaults
15655                                    to the XHTML-compliant non-breaking space character '&#160;')
15656 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15657 progress          Boolean          True to display a progress bar (defaults to false)
15658 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15659 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15660 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15661 title             String           The title text
15662 value             String           The string value to set into the active textbox element if displayed
15663 wait              Boolean          True to display a progress bar (defaults to false)
15664 width             Number           The width of the dialog in pixels
15665 </pre>
15666          *
15667          * Example usage:
15668          * <pre><code>
15669 Roo.Msg.show({
15670    title: 'Address',
15671    msg: 'Please enter your address:',
15672    width: 300,
15673    buttons: Roo.MessageBox.OKCANCEL,
15674    multiline: true,
15675    fn: saveAddress,
15676    animEl: 'addAddressBtn'
15677 });
15678 </code></pre>
15679          * @param {Object} config Configuration options
15680          * @return {Roo.MessageBox} This message box
15681          */
15682         show : function(options){
15683             if(this.isVisible()){
15684                 this.hide();
15685             }
15686             var d = this.getDialog();
15687             opt = options;
15688             d.setTitle(opt.title || "&#160;");
15689             d.close.setDisplayed(opt.closable !== false);
15690             activeTextEl = textboxEl;
15691             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15692             if(opt.prompt){
15693                 if(opt.multiline){
15694                     textboxEl.hide();
15695                     textareaEl.show();
15696                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15697                         opt.multiline : this.defaultTextHeight);
15698                     activeTextEl = textareaEl;
15699                 }else{
15700                     textboxEl.show();
15701                     textareaEl.hide();
15702                 }
15703             }else{
15704                 textboxEl.hide();
15705                 textareaEl.hide();
15706             }
15707             progressEl.setDisplayed(opt.progress === true);
15708             this.updateProgress(0);
15709             activeTextEl.dom.value = opt.value || "";
15710             if(opt.prompt){
15711                 dlg.setDefaultButton(activeTextEl);
15712             }else{
15713                 var bs = opt.buttons;
15714                 var db = null;
15715                 if(bs && bs.ok){
15716                     db = buttons["ok"];
15717                 }else if(bs && bs.yes){
15718                     db = buttons["yes"];
15719                 }
15720                 dlg.setDefaultButton(db);
15721             }
15722             bwidth = updateButtons(opt.buttons);
15723             this.updateText(opt.msg);
15724             if(opt.cls){
15725                 d.el.addClass(opt.cls);
15726             }
15727             d.proxyDrag = opt.proxyDrag === true;
15728             d.modal = opt.modal !== false;
15729             d.mask = opt.modal !== false ? mask : false;
15730             if(!d.isVisible()){
15731                 // force it to the end of the z-index stack so it gets a cursor in FF
15732                 document.body.appendChild(dlg.el.dom);
15733                 d.animateTarget = null;
15734                 d.show(options.animEl);
15735             }
15736             return this;
15737         },
15738
15739         /**
15740          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15741          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15742          * and closing the message box when the process is complete.
15743          * @param {String} title The title bar text
15744          * @param {String} msg The message box body text
15745          * @return {Roo.MessageBox} This message box
15746          */
15747         progress : function(title, msg){
15748             this.show({
15749                 title : title,
15750                 msg : msg,
15751                 buttons: false,
15752                 progress:true,
15753                 closable:false,
15754                 minWidth: this.minProgressWidth,
15755                 modal : true
15756             });
15757             return this;
15758         },
15759
15760         /**
15761          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15762          * If a callback function is passed it will be called after the user clicks the button, and the
15763          * id of the button that was clicked will be passed as the only parameter to the callback
15764          * (could also be the top-right close button).
15765          * @param {String} title The title bar text
15766          * @param {String} msg The message box body text
15767          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15768          * @param {Object} scope (optional) The scope of the callback function
15769          * @return {Roo.MessageBox} This message box
15770          */
15771         alert : function(title, msg, fn, scope){
15772             this.show({
15773                 title : title,
15774                 msg : msg,
15775                 buttons: this.OK,
15776                 fn: fn,
15777                 scope : scope,
15778                 modal : true
15779             });
15780             return this;
15781         },
15782
15783         /**
15784          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15785          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15786          * You are responsible for closing the message box when the process is complete.
15787          * @param {String} msg The message box body text
15788          * @param {String} title (optional) The title bar text
15789          * @return {Roo.MessageBox} This message box
15790          */
15791         wait : function(msg, title){
15792             this.show({
15793                 title : title,
15794                 msg : msg,
15795                 buttons: false,
15796                 closable:false,
15797                 progress:true,
15798                 modal:true,
15799                 width:300,
15800                 wait:true
15801             });
15802             waitTimer = Roo.TaskMgr.start({
15803                 run: function(i){
15804                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15805                 },
15806                 interval: 1000
15807             });
15808             return this;
15809         },
15810
15811         /**
15812          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15813          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15814          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15815          * @param {String} title The title bar text
15816          * @param {String} msg The message box body text
15817          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15818          * @param {Object} scope (optional) The scope of the callback function
15819          * @return {Roo.MessageBox} This message box
15820          */
15821         confirm : function(title, msg, fn, scope){
15822             this.show({
15823                 title : title,
15824                 msg : msg,
15825                 buttons: this.YESNO,
15826                 fn: fn,
15827                 scope : scope,
15828                 modal : true
15829             });
15830             return this;
15831         },
15832
15833         /**
15834          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15835          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15836          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15837          * (could also be the top-right close button) and the text that was entered will be passed as the two
15838          * parameters to the callback.
15839          * @param {String} title The title bar text
15840          * @param {String} msg The message box body text
15841          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15842          * @param {Object} scope (optional) The scope of the callback function
15843          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15844          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15845          * @return {Roo.MessageBox} This message box
15846          */
15847         prompt : function(title, msg, fn, scope, multiline){
15848             this.show({
15849                 title : title,
15850                 msg : msg,
15851                 buttons: this.OKCANCEL,
15852                 fn: fn,
15853                 minWidth:250,
15854                 scope : scope,
15855                 prompt:true,
15856                 multiline: multiline,
15857                 modal : true
15858             });
15859             return this;
15860         },
15861
15862         /**
15863          * Button config that displays a single OK button
15864          * @type Object
15865          */
15866         OK : {ok:true},
15867         /**
15868          * Button config that displays Yes and No buttons
15869          * @type Object
15870          */
15871         YESNO : {yes:true, no:true},
15872         /**
15873          * Button config that displays OK and Cancel buttons
15874          * @type Object
15875          */
15876         OKCANCEL : {ok:true, cancel:true},
15877         /**
15878          * Button config that displays Yes, No and Cancel buttons
15879          * @type Object
15880          */
15881         YESNOCANCEL : {yes:true, no:true, cancel:true},
15882
15883         /**
15884          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15885          * @type Number
15886          */
15887         defaultTextHeight : 75,
15888         /**
15889          * The maximum width in pixels of the message box (defaults to 600)
15890          * @type Number
15891          */
15892         maxWidth : 600,
15893         /**
15894          * The minimum width in pixels of the message box (defaults to 100)
15895          * @type Number
15896          */
15897         minWidth : 100,
15898         /**
15899          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
15900          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
15901          * @type Number
15902          */
15903         minProgressWidth : 250,
15904         /**
15905          * An object containing the default button text strings that can be overriden for localized language support.
15906          * Supported properties are: ok, cancel, yes and no.
15907          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
15908          * @type Object
15909          */
15910         buttonText : {
15911             ok : "OK",
15912             cancel : "Cancel",
15913             yes : "Yes",
15914             no : "No"
15915         }
15916     };
15917 }();
15918
15919 /**
15920  * Shorthand for {@link Roo.MessageBox}
15921  */
15922 Roo.Msg = Roo.MessageBox;/*
15923  * Based on:
15924  * Ext JS Library 1.1.1
15925  * Copyright(c) 2006-2007, Ext JS, LLC.
15926  *
15927  * Originally Released Under LGPL - original licence link has changed is not relivant.
15928  *
15929  * Fork - LGPL
15930  * <script type="text/javascript">
15931  */
15932 /**
15933  * @class Roo.QuickTips
15934  * Provides attractive and customizable tooltips for any element.
15935  * @singleton
15936  */
15937 Roo.QuickTips = function(){
15938     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
15939     var ce, bd, xy, dd;
15940     var visible = false, disabled = true, inited = false;
15941     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
15942     
15943     var onOver = function(e){
15944         if(disabled){
15945             return;
15946         }
15947         var t = e.getTarget();
15948         if(!t || t.nodeType !== 1 || t == document || t == document.body){
15949             return;
15950         }
15951         if(ce && t == ce.el){
15952             clearTimeout(hideProc);
15953             return;
15954         }
15955         if(t && tagEls[t.id]){
15956             tagEls[t.id].el = t;
15957             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
15958             return;
15959         }
15960         var ttp, et = Roo.fly(t);
15961         var ns = cfg.namespace;
15962         if(tm.interceptTitles && t.title){
15963             ttp = t.title;
15964             t.qtip = ttp;
15965             t.removeAttribute("title");
15966             e.preventDefault();
15967         }else{
15968             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
15969         }
15970         if(ttp){
15971             showProc = show.defer(tm.showDelay, tm, [{
15972                 el: t, 
15973                 text: ttp, 
15974                 width: et.getAttributeNS(ns, cfg.width),
15975                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
15976                 title: et.getAttributeNS(ns, cfg.title),
15977                     cls: et.getAttributeNS(ns, cfg.cls)
15978             }]);
15979         }
15980     };
15981     
15982     var onOut = function(e){
15983         clearTimeout(showProc);
15984         var t = e.getTarget();
15985         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
15986             hideProc = setTimeout(hide, tm.hideDelay);
15987         }
15988     };
15989     
15990     var onMove = function(e){
15991         if(disabled){
15992             return;
15993         }
15994         xy = e.getXY();
15995         xy[1] += 18;
15996         if(tm.trackMouse && ce){
15997             el.setXY(xy);
15998         }
15999     };
16000     
16001     var onDown = function(e){
16002         clearTimeout(showProc);
16003         clearTimeout(hideProc);
16004         if(!e.within(el)){
16005             if(tm.hideOnClick){
16006                 hide();
16007                 tm.disable();
16008                 tm.enable.defer(100, tm);
16009             }
16010         }
16011     };
16012     
16013     var getPad = function(){
16014         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
16015     };
16016
16017     var show = function(o){
16018         if(disabled){
16019             return;
16020         }
16021         clearTimeout(dismissProc);
16022         ce = o;
16023         if(removeCls){ // in case manually hidden
16024             el.removeClass(removeCls);
16025             removeCls = null;
16026         }
16027         if(ce.cls){
16028             el.addClass(ce.cls);
16029             removeCls = ce.cls;
16030         }
16031         if(ce.title){
16032             tipTitle.update(ce.title);
16033             tipTitle.show();
16034         }else{
16035             tipTitle.update('');
16036             tipTitle.hide();
16037         }
16038         el.dom.style.width  = tm.maxWidth+'px';
16039         //tipBody.dom.style.width = '';
16040         tipBodyText.update(o.text);
16041         var p = getPad(), w = ce.width;
16042         if(!w){
16043             var td = tipBodyText.dom;
16044             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
16045             if(aw > tm.maxWidth){
16046                 w = tm.maxWidth;
16047             }else if(aw < tm.minWidth){
16048                 w = tm.minWidth;
16049             }else{
16050                 w = aw;
16051             }
16052         }
16053         //tipBody.setWidth(w);
16054         el.setWidth(parseInt(w, 10) + p);
16055         if(ce.autoHide === false){
16056             close.setDisplayed(true);
16057             if(dd){
16058                 dd.unlock();
16059             }
16060         }else{
16061             close.setDisplayed(false);
16062             if(dd){
16063                 dd.lock();
16064             }
16065         }
16066         if(xy){
16067             el.avoidY = xy[1]-18;
16068             el.setXY(xy);
16069         }
16070         if(tm.animate){
16071             el.setOpacity(.1);
16072             el.setStyle("visibility", "visible");
16073             el.fadeIn({callback: afterShow});
16074         }else{
16075             afterShow();
16076         }
16077     };
16078     
16079     var afterShow = function(){
16080         if(ce){
16081             el.show();
16082             esc.enable();
16083             if(tm.autoDismiss && ce.autoHide !== false){
16084                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16085             }
16086         }
16087     };
16088     
16089     var hide = function(noanim){
16090         clearTimeout(dismissProc);
16091         clearTimeout(hideProc);
16092         ce = null;
16093         if(el.isVisible()){
16094             esc.disable();
16095             if(noanim !== true && tm.animate){
16096                 el.fadeOut({callback: afterHide});
16097             }else{
16098                 afterHide();
16099             } 
16100         }
16101     };
16102     
16103     var afterHide = function(){
16104         el.hide();
16105         if(removeCls){
16106             el.removeClass(removeCls);
16107             removeCls = null;
16108         }
16109     };
16110     
16111     return {
16112         /**
16113         * @cfg {Number} minWidth
16114         * The minimum width of the quick tip (defaults to 40)
16115         */
16116        minWidth : 40,
16117         /**
16118         * @cfg {Number} maxWidth
16119         * The maximum width of the quick tip (defaults to 300)
16120         */
16121        maxWidth : 300,
16122         /**
16123         * @cfg {Boolean} interceptTitles
16124         * True to automatically use the element's DOM title value if available (defaults to false)
16125         */
16126        interceptTitles : false,
16127         /**
16128         * @cfg {Boolean} trackMouse
16129         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16130         */
16131        trackMouse : false,
16132         /**
16133         * @cfg {Boolean} hideOnClick
16134         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16135         */
16136        hideOnClick : true,
16137         /**
16138         * @cfg {Number} showDelay
16139         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16140         */
16141        showDelay : 500,
16142         /**
16143         * @cfg {Number} hideDelay
16144         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16145         */
16146        hideDelay : 200,
16147         /**
16148         * @cfg {Boolean} autoHide
16149         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16150         * Used in conjunction with hideDelay.
16151         */
16152        autoHide : true,
16153         /**
16154         * @cfg {Boolean}
16155         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16156         * (defaults to true).  Used in conjunction with autoDismissDelay.
16157         */
16158        autoDismiss : true,
16159         /**
16160         * @cfg {Number}
16161         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16162         */
16163        autoDismissDelay : 5000,
16164        /**
16165         * @cfg {Boolean} animate
16166         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16167         */
16168        animate : false,
16169
16170        /**
16171         * @cfg {String} title
16172         * Title text to display (defaults to '').  This can be any valid HTML markup.
16173         */
16174         title: '',
16175        /**
16176         * @cfg {String} text
16177         * Body text to display (defaults to '').  This can be any valid HTML markup.
16178         */
16179         text : '',
16180        /**
16181         * @cfg {String} cls
16182         * A CSS class to apply to the base quick tip element (defaults to '').
16183         */
16184         cls : '',
16185        /**
16186         * @cfg {Number} width
16187         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16188         * minWidth or maxWidth.
16189         */
16190         width : null,
16191
16192     /**
16193      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16194      * or display QuickTips in a page.
16195      */
16196        init : function(){
16197           tm = Roo.QuickTips;
16198           cfg = tm.tagConfig;
16199           if(!inited){
16200               if(!Roo.isReady){ // allow calling of init() before onReady
16201                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16202                   return;
16203               }
16204               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16205               el.fxDefaults = {stopFx: true};
16206               // maximum custom styling
16207               //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>');
16208               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>');              
16209               tipTitle = el.child('h3');
16210               tipTitle.enableDisplayMode("block");
16211               tipBody = el.child('div.x-tip-bd');
16212               tipBodyText = el.child('div.x-tip-bd-inner');
16213               //bdLeft = el.child('div.x-tip-bd-left');
16214               //bdRight = el.child('div.x-tip-bd-right');
16215               close = el.child('div.x-tip-close');
16216               close.enableDisplayMode("block");
16217               close.on("click", hide);
16218               var d = Roo.get(document);
16219               d.on("mousedown", onDown);
16220               d.on("mouseover", onOver);
16221               d.on("mouseout", onOut);
16222               d.on("mousemove", onMove);
16223               esc = d.addKeyListener(27, hide);
16224               esc.disable();
16225               if(Roo.dd.DD){
16226                   dd = el.initDD("default", null, {
16227                       onDrag : function(){
16228                           el.sync();  
16229                       }
16230                   });
16231                   dd.setHandleElId(tipTitle.id);
16232                   dd.lock();
16233               }
16234               inited = true;
16235           }
16236           this.enable(); 
16237        },
16238
16239     /**
16240      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16241      * are supported:
16242      * <pre>
16243 Property    Type                   Description
16244 ----------  ---------------------  ------------------------------------------------------------------------
16245 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16246      * </ul>
16247      * @param {Object} config The config object
16248      */
16249        register : function(config){
16250            var cs = config instanceof Array ? config : arguments;
16251            for(var i = 0, len = cs.length; i < len; i++) {
16252                var c = cs[i];
16253                var target = c.target;
16254                if(target){
16255                    if(target instanceof Array){
16256                        for(var j = 0, jlen = target.length; j < jlen; j++){
16257                            tagEls[target[j]] = c;
16258                        }
16259                    }else{
16260                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16261                    }
16262                }
16263            }
16264        },
16265
16266     /**
16267      * Removes this quick tip from its element and destroys it.
16268      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16269      */
16270        unregister : function(el){
16271            delete tagEls[Roo.id(el)];
16272        },
16273
16274     /**
16275      * Enable this quick tip.
16276      */
16277        enable : function(){
16278            if(inited && disabled){
16279                locks.pop();
16280                if(locks.length < 1){
16281                    disabled = false;
16282                }
16283            }
16284        },
16285
16286     /**
16287      * Disable this quick tip.
16288      */
16289        disable : function(){
16290           disabled = true;
16291           clearTimeout(showProc);
16292           clearTimeout(hideProc);
16293           clearTimeout(dismissProc);
16294           if(ce){
16295               hide(true);
16296           }
16297           locks.push(1);
16298        },
16299
16300     /**
16301      * Returns true if the quick tip is enabled, else false.
16302      */
16303        isEnabled : function(){
16304             return !disabled;
16305        },
16306
16307         // private
16308        tagConfig : {
16309            namespace : "ext",
16310            attribute : "qtip",
16311            width : "width",
16312            target : "target",
16313            title : "qtitle",
16314            hide : "hide",
16315            cls : "qclass"
16316        }
16317    };
16318 }();
16319
16320 // backwards compat
16321 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16322  * Based on:
16323  * Ext JS Library 1.1.1
16324  * Copyright(c) 2006-2007, Ext JS, LLC.
16325  *
16326  * Originally Released Under LGPL - original licence link has changed is not relivant.
16327  *
16328  * Fork - LGPL
16329  * <script type="text/javascript">
16330  */
16331  
16332
16333 /**
16334  * @class Roo.tree.TreePanel
16335  * @extends Roo.data.Tree
16336
16337  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16338  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16339  * @cfg {Boolean} enableDD true to enable drag and drop
16340  * @cfg {Boolean} enableDrag true to enable just drag
16341  * @cfg {Boolean} enableDrop true to enable just drop
16342  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16343  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16344  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16345  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16346  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16347  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16348  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16349  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16350  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16351  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16352  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16353  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16354  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16355  * @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>
16356  * @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>
16357  * 
16358  * @constructor
16359  * @param {String/HTMLElement/Element} el The container element
16360  * @param {Object} config
16361  */
16362 Roo.tree.TreePanel = function(el, config){
16363     var root = false;
16364     var loader = false;
16365     if (config.root) {
16366         root = config.root;
16367         delete config.root;
16368     }
16369     if (config.loader) {
16370         loader = config.loader;
16371         delete config.loader;
16372     }
16373     
16374     Roo.apply(this, config);
16375     Roo.tree.TreePanel.superclass.constructor.call(this);
16376     this.el = Roo.get(el);
16377     this.el.addClass('x-tree');
16378     //console.log(root);
16379     if (root) {
16380         this.setRootNode( Roo.factory(root, Roo.tree));
16381     }
16382     if (loader) {
16383         this.loader = Roo.factory(loader, Roo.tree);
16384     }
16385    /**
16386     * Read-only. The id of the container element becomes this TreePanel's id.
16387     */
16388    this.id = this.el.id;
16389    this.addEvents({
16390         /**
16391         * @event beforeload
16392         * Fires before a node is loaded, return false to cancel
16393         * @param {Node} node The node being loaded
16394         */
16395         "beforeload" : true,
16396         /**
16397         * @event load
16398         * Fires when a node is loaded
16399         * @param {Node} node The node that was loaded
16400         */
16401         "load" : true,
16402         /**
16403         * @event textchange
16404         * Fires when the text for a node is changed
16405         * @param {Node} node The node
16406         * @param {String} text The new text
16407         * @param {String} oldText The old text
16408         */
16409         "textchange" : true,
16410         /**
16411         * @event beforeexpand
16412         * Fires before a node is expanded, return false to cancel.
16413         * @param {Node} node The node
16414         * @param {Boolean} deep
16415         * @param {Boolean} anim
16416         */
16417         "beforeexpand" : true,
16418         /**
16419         * @event beforecollapse
16420         * Fires before a node is collapsed, return false to cancel.
16421         * @param {Node} node The node
16422         * @param {Boolean} deep
16423         * @param {Boolean} anim
16424         */
16425         "beforecollapse" : true,
16426         /**
16427         * @event expand
16428         * Fires when a node is expanded
16429         * @param {Node} node The node
16430         */
16431         "expand" : true,
16432         /**
16433         * @event disabledchange
16434         * Fires when the disabled status of a node changes
16435         * @param {Node} node The node
16436         * @param {Boolean} disabled
16437         */
16438         "disabledchange" : true,
16439         /**
16440         * @event collapse
16441         * Fires when a node is collapsed
16442         * @param {Node} node The node
16443         */
16444         "collapse" : true,
16445         /**
16446         * @event beforeclick
16447         * Fires before click processing on a node. Return false to cancel the default action.
16448         * @param {Node} node The node
16449         * @param {Roo.EventObject} e The event object
16450         */
16451         "beforeclick":true,
16452         /**
16453         * @event checkchange
16454         * Fires when a node with a checkbox's checked property changes
16455         * @param {Node} this This node
16456         * @param {Boolean} checked
16457         */
16458         "checkchange":true,
16459         /**
16460         * @event click
16461         * Fires when a node is clicked
16462         * @param {Node} node The node
16463         * @param {Roo.EventObject} e The event object
16464         */
16465         "click":true,
16466         /**
16467         * @event dblclick
16468         * Fires when a node is double clicked
16469         * @param {Node} node The node
16470         * @param {Roo.EventObject} e The event object
16471         */
16472         "dblclick":true,
16473         /**
16474         * @event contextmenu
16475         * Fires when a node is right clicked
16476         * @param {Node} node The node
16477         * @param {Roo.EventObject} e The event object
16478         */
16479         "contextmenu":true,
16480         /**
16481         * @event beforechildrenrendered
16482         * Fires right before the child nodes for a node are rendered
16483         * @param {Node} node The node
16484         */
16485         "beforechildrenrendered":true,
16486        /**
16487              * @event startdrag
16488              * Fires when a node starts being dragged
16489              * @param {Roo.tree.TreePanel} this
16490              * @param {Roo.tree.TreeNode} node
16491              * @param {event} e The raw browser event
16492              */ 
16493             "startdrag" : true,
16494             /**
16495              * @event enddrag
16496              * Fires when a drag operation is complete
16497              * @param {Roo.tree.TreePanel} this
16498              * @param {Roo.tree.TreeNode} node
16499              * @param {event} e The raw browser event
16500              */
16501             "enddrag" : true,
16502             /**
16503              * @event dragdrop
16504              * Fires when a dragged node is dropped on a valid DD target
16505              * @param {Roo.tree.TreePanel} this
16506              * @param {Roo.tree.TreeNode} node
16507              * @param {DD} dd The dd it was dropped on
16508              * @param {event} e The raw browser event
16509              */
16510             "dragdrop" : true,
16511             /**
16512              * @event beforenodedrop
16513              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16514              * passed to handlers has the following properties:<br />
16515              * <ul style="padding:5px;padding-left:16px;">
16516              * <li>tree - The TreePanel</li>
16517              * <li>target - The node being targeted for the drop</li>
16518              * <li>data - The drag data from the drag source</li>
16519              * <li>point - The point of the drop - append, above or below</li>
16520              * <li>source - The drag source</li>
16521              * <li>rawEvent - Raw mouse event</li>
16522              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16523              * to be inserted by setting them on this object.</li>
16524              * <li>cancel - Set this to true to cancel the drop.</li>
16525              * </ul>
16526              * @param {Object} dropEvent
16527              */
16528             "beforenodedrop" : true,
16529             /**
16530              * @event nodedrop
16531              * Fires after a DD object is dropped on a node in this tree. The dropEvent
16532              * passed to handlers has the following properties:<br />
16533              * <ul style="padding:5px;padding-left:16px;">
16534              * <li>tree - The TreePanel</li>
16535              * <li>target - The node being targeted for the drop</li>
16536              * <li>data - The drag data from the drag source</li>
16537              * <li>point - The point of the drop - append, above or below</li>
16538              * <li>source - The drag source</li>
16539              * <li>rawEvent - Raw mouse event</li>
16540              * <li>dropNode - Dropped node(s).</li>
16541              * </ul>
16542              * @param {Object} dropEvent
16543              */
16544             "nodedrop" : true,
16545              /**
16546              * @event nodedragover
16547              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16548              * passed to handlers has the following properties:<br />
16549              * <ul style="padding:5px;padding-left:16px;">
16550              * <li>tree - The TreePanel</li>
16551              * <li>target - The node being targeted for the drop</li>
16552              * <li>data - The drag data from the drag source</li>
16553              * <li>point - The point of the drop - append, above or below</li>
16554              * <li>source - The drag source</li>
16555              * <li>rawEvent - Raw mouse event</li>
16556              * <li>dropNode - Drop node(s) provided by the source.</li>
16557              * <li>cancel - Set this to true to signal drop not allowed.</li>
16558              * </ul>
16559              * @param {Object} dragOverEvent
16560              */
16561             "nodedragover" : true
16562         
16563    });
16564    if(this.singleExpand){
16565        this.on("beforeexpand", this.restrictExpand, this);
16566    }
16567 };
16568 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16569     rootVisible : true,
16570     animate: Roo.enableFx,
16571     lines : true,
16572     enableDD : false,
16573     hlDrop : Roo.enableFx,
16574   
16575     renderer: false,
16576     
16577     rendererTip: false,
16578     // private
16579     restrictExpand : function(node){
16580         var p = node.parentNode;
16581         if(p){
16582             if(p.expandedChild && p.expandedChild.parentNode == p){
16583                 p.expandedChild.collapse();
16584             }
16585             p.expandedChild = node;
16586         }
16587     },
16588
16589     // private override
16590     setRootNode : function(node){
16591         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16592         if(!this.rootVisible){
16593             node.ui = new Roo.tree.RootTreeNodeUI(node);
16594         }
16595         return node;
16596     },
16597
16598     /**
16599      * Returns the container element for this TreePanel
16600      */
16601     getEl : function(){
16602         return this.el;
16603     },
16604
16605     /**
16606      * Returns the default TreeLoader for this TreePanel
16607      */
16608     getLoader : function(){
16609         return this.loader;
16610     },
16611
16612     /**
16613      * Expand all nodes
16614      */
16615     expandAll : function(){
16616         this.root.expand(true);
16617     },
16618
16619     /**
16620      * Collapse all nodes
16621      */
16622     collapseAll : function(){
16623         this.root.collapse(true);
16624     },
16625
16626     /**
16627      * Returns the selection model used by this TreePanel
16628      */
16629     getSelectionModel : function(){
16630         if(!this.selModel){
16631             this.selModel = new Roo.tree.DefaultSelectionModel();
16632         }
16633         return this.selModel;
16634     },
16635
16636     /**
16637      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16638      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16639      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16640      * @return {Array}
16641      */
16642     getChecked : function(a, startNode){
16643         startNode = startNode || this.root;
16644         var r = [];
16645         var f = function(){
16646             if(this.attributes.checked){
16647                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16648             }
16649         }
16650         startNode.cascade(f);
16651         return r;
16652     },
16653
16654     /**
16655      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16656      * @param {String} path
16657      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16658      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16659      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16660      */
16661     expandPath : function(path, attr, callback){
16662         attr = attr || "id";
16663         var keys = path.split(this.pathSeparator);
16664         var curNode = this.root;
16665         if(curNode.attributes[attr] != keys[1]){ // invalid root
16666             if(callback){
16667                 callback(false, null);
16668             }
16669             return;
16670         }
16671         var index = 1;
16672         var f = function(){
16673             if(++index == keys.length){
16674                 if(callback){
16675                     callback(true, curNode);
16676                 }
16677                 return;
16678             }
16679             var c = curNode.findChild(attr, keys[index]);
16680             if(!c){
16681                 if(callback){
16682                     callback(false, curNode);
16683                 }
16684                 return;
16685             }
16686             curNode = c;
16687             c.expand(false, false, f);
16688         };
16689         curNode.expand(false, false, f);
16690     },
16691
16692     /**
16693      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16694      * @param {String} path
16695      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16696      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16697      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16698      */
16699     selectPath : function(path, attr, callback){
16700         attr = attr || "id";
16701         var keys = path.split(this.pathSeparator);
16702         var v = keys.pop();
16703         if(keys.length > 0){
16704             var f = function(success, node){
16705                 if(success && node){
16706                     var n = node.findChild(attr, v);
16707                     if(n){
16708                         n.select();
16709                         if(callback){
16710                             callback(true, n);
16711                         }
16712                     }else if(callback){
16713                         callback(false, n);
16714                     }
16715                 }else{
16716                     if(callback){
16717                         callback(false, n);
16718                     }
16719                 }
16720             };
16721             this.expandPath(keys.join(this.pathSeparator), attr, f);
16722         }else{
16723             this.root.select();
16724             if(callback){
16725                 callback(true, this.root);
16726             }
16727         }
16728     },
16729
16730     getTreeEl : function(){
16731         return this.el;
16732     },
16733
16734     /**
16735      * Trigger rendering of this TreePanel
16736      */
16737     render : function(){
16738         if (this.innerCt) {
16739             return this; // stop it rendering more than once!!
16740         }
16741         
16742         this.innerCt = this.el.createChild({tag:"ul",
16743                cls:"x-tree-root-ct " +
16744                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16745
16746         if(this.containerScroll){
16747             Roo.dd.ScrollManager.register(this.el);
16748         }
16749         if((this.enableDD || this.enableDrop) && !this.dropZone){
16750            /**
16751             * The dropZone used by this tree if drop is enabled
16752             * @type Roo.tree.TreeDropZone
16753             */
16754              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16755                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16756            });
16757         }
16758         if((this.enableDD || this.enableDrag) && !this.dragZone){
16759            /**
16760             * The dragZone used by this tree if drag is enabled
16761             * @type Roo.tree.TreeDragZone
16762             */
16763             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16764                ddGroup: this.ddGroup || "TreeDD",
16765                scroll: this.ddScroll
16766            });
16767         }
16768         this.getSelectionModel().init(this);
16769         if (!this.root) {
16770             console.log("ROOT not set in tree");
16771             return;
16772         }
16773         this.root.render();
16774         if(!this.rootVisible){
16775             this.root.renderChildren();
16776         }
16777         return this;
16778     }
16779 });/*
16780  * Based on:
16781  * Ext JS Library 1.1.1
16782  * Copyright(c) 2006-2007, Ext JS, LLC.
16783  *
16784  * Originally Released Under LGPL - original licence link has changed is not relivant.
16785  *
16786  * Fork - LGPL
16787  * <script type="text/javascript">
16788  */
16789  
16790
16791 /**
16792  * @class Roo.tree.DefaultSelectionModel
16793  * @extends Roo.util.Observable
16794  * The default single selection for a TreePanel.
16795  */
16796 Roo.tree.DefaultSelectionModel = function(){
16797    this.selNode = null;
16798    
16799    this.addEvents({
16800        /**
16801         * @event selectionchange
16802         * Fires when the selected node changes
16803         * @param {DefaultSelectionModel} this
16804         * @param {TreeNode} node the new selection
16805         */
16806        "selectionchange" : true,
16807
16808        /**
16809         * @event beforeselect
16810         * Fires before the selected node changes, return false to cancel the change
16811         * @param {DefaultSelectionModel} this
16812         * @param {TreeNode} node the new selection
16813         * @param {TreeNode} node the old selection
16814         */
16815        "beforeselect" : true
16816    });
16817 };
16818
16819 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16820     init : function(tree){
16821         this.tree = tree;
16822         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16823         tree.on("click", this.onNodeClick, this);
16824     },
16825     
16826     onNodeClick : function(node, e){
16827         if (e.ctrlKey && this.selNode == node)  {
16828             this.unselect(node);
16829             return;
16830         }
16831         this.select(node);
16832     },
16833     
16834     /**
16835      * Select a node.
16836      * @param {TreeNode} node The node to select
16837      * @return {TreeNode} The selected node
16838      */
16839     select : function(node){
16840         var last = this.selNode;
16841         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16842             if(last){
16843                 last.ui.onSelectedChange(false);
16844             }
16845             this.selNode = node;
16846             node.ui.onSelectedChange(true);
16847             this.fireEvent("selectionchange", this, node, last);
16848         }
16849         return node;
16850     },
16851     
16852     /**
16853      * Deselect a node.
16854      * @param {TreeNode} node The node to unselect
16855      */
16856     unselect : function(node){
16857         if(this.selNode == node){
16858             this.clearSelections();
16859         }    
16860     },
16861     
16862     /**
16863      * Clear all selections
16864      */
16865     clearSelections : function(){
16866         var n = this.selNode;
16867         if(n){
16868             n.ui.onSelectedChange(false);
16869             this.selNode = null;
16870             this.fireEvent("selectionchange", this, null);
16871         }
16872         return n;
16873     },
16874     
16875     /**
16876      * Get the selected node
16877      * @return {TreeNode} The selected node
16878      */
16879     getSelectedNode : function(){
16880         return this.selNode;    
16881     },
16882     
16883     /**
16884      * Returns true if the node is selected
16885      * @param {TreeNode} node The node to check
16886      * @return {Boolean}
16887      */
16888     isSelected : function(node){
16889         return this.selNode == node;  
16890     },
16891
16892     /**
16893      * Selects the node above the selected node in the tree, intelligently walking the nodes
16894      * @return TreeNode The new selection
16895      */
16896     selectPrevious : function(){
16897         var s = this.selNode || this.lastSelNode;
16898         if(!s){
16899             return null;
16900         }
16901         var ps = s.previousSibling;
16902         if(ps){
16903             if(!ps.isExpanded() || ps.childNodes.length < 1){
16904                 return this.select(ps);
16905             } else{
16906                 var lc = ps.lastChild;
16907                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
16908                     lc = lc.lastChild;
16909                 }
16910                 return this.select(lc);
16911             }
16912         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
16913             return this.select(s.parentNode);
16914         }
16915         return null;
16916     },
16917
16918     /**
16919      * Selects the node above the selected node in the tree, intelligently walking the nodes
16920      * @return TreeNode The new selection
16921      */
16922     selectNext : function(){
16923         var s = this.selNode || this.lastSelNode;
16924         if(!s){
16925             return null;
16926         }
16927         if(s.firstChild && s.isExpanded()){
16928              return this.select(s.firstChild);
16929          }else if(s.nextSibling){
16930              return this.select(s.nextSibling);
16931          }else if(s.parentNode){
16932             var newS = null;
16933             s.parentNode.bubble(function(){
16934                 if(this.nextSibling){
16935                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
16936                     return false;
16937                 }
16938             });
16939             return newS;
16940          }
16941         return null;
16942     },
16943
16944     onKeyDown : function(e){
16945         var s = this.selNode || this.lastSelNode;
16946         // undesirable, but required
16947         var sm = this;
16948         if(!s){
16949             return;
16950         }
16951         var k = e.getKey();
16952         switch(k){
16953              case e.DOWN:
16954                  e.stopEvent();
16955                  this.selectNext();
16956              break;
16957              case e.UP:
16958                  e.stopEvent();
16959                  this.selectPrevious();
16960              break;
16961              case e.RIGHT:
16962                  e.preventDefault();
16963                  if(s.hasChildNodes()){
16964                      if(!s.isExpanded()){
16965                          s.expand();
16966                      }else if(s.firstChild){
16967                          this.select(s.firstChild, e);
16968                      }
16969                  }
16970              break;
16971              case e.LEFT:
16972                  e.preventDefault();
16973                  if(s.hasChildNodes() && s.isExpanded()){
16974                      s.collapse();
16975                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
16976                      this.select(s.parentNode, e);
16977                  }
16978              break;
16979         };
16980     }
16981 });
16982
16983 /**
16984  * @class Roo.tree.MultiSelectionModel
16985  * @extends Roo.util.Observable
16986  * Multi selection for a TreePanel.
16987  */
16988 Roo.tree.MultiSelectionModel = function(){
16989    this.selNodes = [];
16990    this.selMap = {};
16991    this.addEvents({
16992        /**
16993         * @event selectionchange
16994         * Fires when the selected nodes change
16995         * @param {MultiSelectionModel} this
16996         * @param {Array} nodes Array of the selected nodes
16997         */
16998        "selectionchange" : true
16999    });
17000 };
17001
17002 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
17003     init : function(tree){
17004         this.tree = tree;
17005         tree.getTreeEl().on("keydown", this.onKeyDown, this);
17006         tree.on("click", this.onNodeClick, this);
17007     },
17008     
17009     onNodeClick : function(node, e){
17010         this.select(node, e, e.ctrlKey);
17011     },
17012     
17013     /**
17014      * Select a node.
17015      * @param {TreeNode} node The node to select
17016      * @param {EventObject} e (optional) An event associated with the selection
17017      * @param {Boolean} keepExisting True to retain existing selections
17018      * @return {TreeNode} The selected node
17019      */
17020     select : function(node, e, keepExisting){
17021         if(keepExisting !== true){
17022             this.clearSelections(true);
17023         }
17024         if(this.isSelected(node)){
17025             this.lastSelNode = node;
17026             return node;
17027         }
17028         this.selNodes.push(node);
17029         this.selMap[node.id] = node;
17030         this.lastSelNode = node;
17031         node.ui.onSelectedChange(true);
17032         this.fireEvent("selectionchange", this, this.selNodes);
17033         return node;
17034     },
17035     
17036     /**
17037      * Deselect a node.
17038      * @param {TreeNode} node The node to unselect
17039      */
17040     unselect : function(node){
17041         if(this.selMap[node.id]){
17042             node.ui.onSelectedChange(false);
17043             var sn = this.selNodes;
17044             var index = -1;
17045             if(sn.indexOf){
17046                 index = sn.indexOf(node);
17047             }else{
17048                 for(var i = 0, len = sn.length; i < len; i++){
17049                     if(sn[i] == node){
17050                         index = i;
17051                         break;
17052                     }
17053                 }
17054             }
17055             if(index != -1){
17056                 this.selNodes.splice(index, 1);
17057             }
17058             delete this.selMap[node.id];
17059             this.fireEvent("selectionchange", this, this.selNodes);
17060         }
17061     },
17062     
17063     /**
17064      * Clear all selections
17065      */
17066     clearSelections : function(suppressEvent){
17067         var sn = this.selNodes;
17068         if(sn.length > 0){
17069             for(var i = 0, len = sn.length; i < len; i++){
17070                 sn[i].ui.onSelectedChange(false);
17071             }
17072             this.selNodes = [];
17073             this.selMap = {};
17074             if(suppressEvent !== true){
17075                 this.fireEvent("selectionchange", this, this.selNodes);
17076             }
17077         }
17078     },
17079     
17080     /**
17081      * Returns true if the node is selected
17082      * @param {TreeNode} node The node to check
17083      * @return {Boolean}
17084      */
17085     isSelected : function(node){
17086         return this.selMap[node.id] ? true : false;  
17087     },
17088     
17089     /**
17090      * Returns an array of the selected nodes
17091      * @return {Array}
17092      */
17093     getSelectedNodes : function(){
17094         return this.selNodes;    
17095     },
17096
17097     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17098
17099     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17100
17101     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17102 });/*
17103  * Based on:
17104  * Ext JS Library 1.1.1
17105  * Copyright(c) 2006-2007, Ext JS, LLC.
17106  *
17107  * Originally Released Under LGPL - original licence link has changed is not relivant.
17108  *
17109  * Fork - LGPL
17110  * <script type="text/javascript">
17111  */
17112  
17113 /**
17114  * @class Roo.tree.TreeNode
17115  * @extends Roo.data.Node
17116  * @cfg {String} text The text for this node
17117  * @cfg {Boolean} expanded true to start the node expanded
17118  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17119  * @cfg {Boolean} allowDrop false if this node cannot be drop on
17120  * @cfg {Boolean} disabled true to start the node disabled
17121  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17122  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17123  * @cfg {String} cls A css class to be added to the node
17124  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17125  * @cfg {String} href URL of the link used for the node (defaults to #)
17126  * @cfg {String} hrefTarget target frame for the link
17127  * @cfg {String} qtip An Ext QuickTip for the node
17128  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17129  * @cfg {Boolean} singleClickExpand True for single click expand on this node
17130  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17131  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17132  * (defaults to undefined with no checkbox rendered)
17133  * @constructor
17134  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17135  */
17136 Roo.tree.TreeNode = function(attributes){
17137     attributes = attributes || {};
17138     if(typeof attributes == "string"){
17139         attributes = {text: attributes};
17140     }
17141     this.childrenRendered = false;
17142     this.rendered = false;
17143     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17144     this.expanded = attributes.expanded === true;
17145     this.isTarget = attributes.isTarget !== false;
17146     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17147     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17148
17149     /**
17150      * Read-only. The text for this node. To change it use setText().
17151      * @type String
17152      */
17153     this.text = attributes.text;
17154     /**
17155      * True if this node is disabled.
17156      * @type Boolean
17157      */
17158     this.disabled = attributes.disabled === true;
17159
17160     this.addEvents({
17161         /**
17162         * @event textchange
17163         * Fires when the text for this node is changed
17164         * @param {Node} this This node
17165         * @param {String} text The new text
17166         * @param {String} oldText The old text
17167         */
17168         "textchange" : true,
17169         /**
17170         * @event beforeexpand
17171         * Fires before this node is expanded, return false to cancel.
17172         * @param {Node} this This node
17173         * @param {Boolean} deep
17174         * @param {Boolean} anim
17175         */
17176         "beforeexpand" : true,
17177         /**
17178         * @event beforecollapse
17179         * Fires before this node is collapsed, return false to cancel.
17180         * @param {Node} this This node
17181         * @param {Boolean} deep
17182         * @param {Boolean} anim
17183         */
17184         "beforecollapse" : true,
17185         /**
17186         * @event expand
17187         * Fires when this node is expanded
17188         * @param {Node} this This node
17189         */
17190         "expand" : true,
17191         /**
17192         * @event disabledchange
17193         * Fires when the disabled status of this node changes
17194         * @param {Node} this This node
17195         * @param {Boolean} disabled
17196         */
17197         "disabledchange" : true,
17198         /**
17199         * @event collapse
17200         * Fires when this node is collapsed
17201         * @param {Node} this This node
17202         */
17203         "collapse" : true,
17204         /**
17205         * @event beforeclick
17206         * Fires before click processing. Return false to cancel the default action.
17207         * @param {Node} this This node
17208         * @param {Roo.EventObject} e The event object
17209         */
17210         "beforeclick":true,
17211         /**
17212         * @event checkchange
17213         * Fires when a node with a checkbox's checked property changes
17214         * @param {Node} this This node
17215         * @param {Boolean} checked
17216         */
17217         "checkchange":true,
17218         /**
17219         * @event click
17220         * Fires when this node is clicked
17221         * @param {Node} this This node
17222         * @param {Roo.EventObject} e The event object
17223         */
17224         "click":true,
17225         /**
17226         * @event dblclick
17227         * Fires when this node is double clicked
17228         * @param {Node} this This node
17229         * @param {Roo.EventObject} e The event object
17230         */
17231         "dblclick":true,
17232         /**
17233         * @event contextmenu
17234         * Fires when this node is right clicked
17235         * @param {Node} this This node
17236         * @param {Roo.EventObject} e The event object
17237         */
17238         "contextmenu":true,
17239         /**
17240         * @event beforechildrenrendered
17241         * Fires right before the child nodes for this node are rendered
17242         * @param {Node} this This node
17243         */
17244         "beforechildrenrendered":true
17245     });
17246
17247     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17248
17249     /**
17250      * Read-only. The UI for this node
17251      * @type TreeNodeUI
17252      */
17253     this.ui = new uiClass(this);
17254 };
17255 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17256     preventHScroll: true,
17257     /**
17258      * Returns true if this node is expanded
17259      * @return {Boolean}
17260      */
17261     isExpanded : function(){
17262         return this.expanded;
17263     },
17264
17265     /**
17266      * Returns the UI object for this node
17267      * @return {TreeNodeUI}
17268      */
17269     getUI : function(){
17270         return this.ui;
17271     },
17272
17273     // private override
17274     setFirstChild : function(node){
17275         var of = this.firstChild;
17276         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17277         if(this.childrenRendered && of && node != of){
17278             of.renderIndent(true, true);
17279         }
17280         if(this.rendered){
17281             this.renderIndent(true, true);
17282         }
17283     },
17284
17285     // private override
17286     setLastChild : function(node){
17287         var ol = this.lastChild;
17288         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17289         if(this.childrenRendered && ol && node != ol){
17290             ol.renderIndent(true, true);
17291         }
17292         if(this.rendered){
17293             this.renderIndent(true, true);
17294         }
17295     },
17296
17297     // these methods are overridden to provide lazy rendering support
17298     // private override
17299     appendChild : function(){
17300         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17301         if(node && this.childrenRendered){
17302             node.render();
17303         }
17304         this.ui.updateExpandIcon();
17305         return node;
17306     },
17307
17308     // private override
17309     removeChild : function(node){
17310         this.ownerTree.getSelectionModel().unselect(node);
17311         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17312         // if it's been rendered remove dom node
17313         if(this.childrenRendered){
17314             node.ui.remove();
17315         }
17316         if(this.childNodes.length < 1){
17317             this.collapse(false, false);
17318         }else{
17319             this.ui.updateExpandIcon();
17320         }
17321         if(!this.firstChild) {
17322             this.childrenRendered = false;
17323         }
17324         return node;
17325     },
17326
17327     // private override
17328     insertBefore : function(node, refNode){
17329         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17330         if(newNode && refNode && this.childrenRendered){
17331             node.render();
17332         }
17333         this.ui.updateExpandIcon();
17334         return newNode;
17335     },
17336
17337     /**
17338      * Sets the text for this node
17339      * @param {String} text
17340      */
17341     setText : function(text){
17342         var oldText = this.text;
17343         this.text = text;
17344         this.attributes.text = text;
17345         if(this.rendered){ // event without subscribing
17346             this.ui.onTextChange(this, text, oldText);
17347         }
17348         this.fireEvent("textchange", this, text, oldText);
17349     },
17350
17351     /**
17352      * Triggers selection of this node
17353      */
17354     select : function(){
17355         this.getOwnerTree().getSelectionModel().select(this);
17356     },
17357
17358     /**
17359      * Triggers deselection of this node
17360      */
17361     unselect : function(){
17362         this.getOwnerTree().getSelectionModel().unselect(this);
17363     },
17364
17365     /**
17366      * Returns true if this node is selected
17367      * @return {Boolean}
17368      */
17369     isSelected : function(){
17370         return this.getOwnerTree().getSelectionModel().isSelected(this);
17371     },
17372
17373     /**
17374      * Expand this node.
17375      * @param {Boolean} deep (optional) True to expand all children as well
17376      * @param {Boolean} anim (optional) false to cancel the default animation
17377      * @param {Function} callback (optional) A callback to be called when
17378      * expanding this node completes (does not wait for deep expand to complete).
17379      * Called with 1 parameter, this node.
17380      */
17381     expand : function(deep, anim, callback){
17382         if(!this.expanded){
17383             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17384                 return;
17385             }
17386             if(!this.childrenRendered){
17387                 this.renderChildren();
17388             }
17389             this.expanded = true;
17390             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17391                 this.ui.animExpand(function(){
17392                     this.fireEvent("expand", this);
17393                     if(typeof callback == "function"){
17394                         callback(this);
17395                     }
17396                     if(deep === true){
17397                         this.expandChildNodes(true);
17398                     }
17399                 }.createDelegate(this));
17400                 return;
17401             }else{
17402                 this.ui.expand();
17403                 this.fireEvent("expand", this);
17404                 if(typeof callback == "function"){
17405                     callback(this);
17406                 }
17407             }
17408         }else{
17409            if(typeof callback == "function"){
17410                callback(this);
17411            }
17412         }
17413         if(deep === true){
17414             this.expandChildNodes(true);
17415         }
17416     },
17417
17418     isHiddenRoot : function(){
17419         return this.isRoot && !this.getOwnerTree().rootVisible;
17420     },
17421
17422     /**
17423      * Collapse this node.
17424      * @param {Boolean} deep (optional) True to collapse all children as well
17425      * @param {Boolean} anim (optional) false to cancel the default animation
17426      */
17427     collapse : function(deep, anim){
17428         if(this.expanded && !this.isHiddenRoot()){
17429             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17430                 return;
17431             }
17432             this.expanded = false;
17433             if((this.getOwnerTree().animate && anim !== false) || anim){
17434                 this.ui.animCollapse(function(){
17435                     this.fireEvent("collapse", this);
17436                     if(deep === true){
17437                         this.collapseChildNodes(true);
17438                     }
17439                 }.createDelegate(this));
17440                 return;
17441             }else{
17442                 this.ui.collapse();
17443                 this.fireEvent("collapse", this);
17444             }
17445         }
17446         if(deep === true){
17447             var cs = this.childNodes;
17448             for(var i = 0, len = cs.length; i < len; i++) {
17449                 cs[i].collapse(true, false);
17450             }
17451         }
17452     },
17453
17454     // private
17455     delayedExpand : function(delay){
17456         if(!this.expandProcId){
17457             this.expandProcId = this.expand.defer(delay, this);
17458         }
17459     },
17460
17461     // private
17462     cancelExpand : function(){
17463         if(this.expandProcId){
17464             clearTimeout(this.expandProcId);
17465         }
17466         this.expandProcId = false;
17467     },
17468
17469     /**
17470      * Toggles expanded/collapsed state of the node
17471      */
17472     toggle : function(){
17473         if(this.expanded){
17474             this.collapse();
17475         }else{
17476             this.expand();
17477         }
17478     },
17479
17480     /**
17481      * Ensures all parent nodes are expanded
17482      */
17483     ensureVisible : function(callback){
17484         var tree = this.getOwnerTree();
17485         tree.expandPath(this.parentNode.getPath(), false, function(){
17486             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17487             Roo.callback(callback);
17488         }.createDelegate(this));
17489     },
17490
17491     /**
17492      * Expand all child nodes
17493      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17494      */
17495     expandChildNodes : function(deep){
17496         var cs = this.childNodes;
17497         for(var i = 0, len = cs.length; i < len; i++) {
17498                 cs[i].expand(deep);
17499         }
17500     },
17501
17502     /**
17503      * Collapse all child nodes
17504      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17505      */
17506     collapseChildNodes : function(deep){
17507         var cs = this.childNodes;
17508         for(var i = 0, len = cs.length; i < len; i++) {
17509                 cs[i].collapse(deep);
17510         }
17511     },
17512
17513     /**
17514      * Disables this node
17515      */
17516     disable : function(){
17517         this.disabled = true;
17518         this.unselect();
17519         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17520             this.ui.onDisableChange(this, true);
17521         }
17522         this.fireEvent("disabledchange", this, true);
17523     },
17524
17525     /**
17526      * Enables this node
17527      */
17528     enable : function(){
17529         this.disabled = false;
17530         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17531             this.ui.onDisableChange(this, false);
17532         }
17533         this.fireEvent("disabledchange", this, false);
17534     },
17535
17536     // private
17537     renderChildren : function(suppressEvent){
17538         if(suppressEvent !== false){
17539             this.fireEvent("beforechildrenrendered", this);
17540         }
17541         var cs = this.childNodes;
17542         for(var i = 0, len = cs.length; i < len; i++){
17543             cs[i].render(true);
17544         }
17545         this.childrenRendered = true;
17546     },
17547
17548     // private
17549     sort : function(fn, scope){
17550         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17551         if(this.childrenRendered){
17552             var cs = this.childNodes;
17553             for(var i = 0, len = cs.length; i < len; i++){
17554                 cs[i].render(true);
17555             }
17556         }
17557     },
17558
17559     // private
17560     render : function(bulkRender){
17561         this.ui.render(bulkRender);
17562         if(!this.rendered){
17563             this.rendered = true;
17564             if(this.expanded){
17565                 this.expanded = false;
17566                 this.expand(false, false);
17567             }
17568         }
17569     },
17570
17571     // private
17572     renderIndent : function(deep, refresh){
17573         if(refresh){
17574             this.ui.childIndent = null;
17575         }
17576         this.ui.renderIndent();
17577         if(deep === true && this.childrenRendered){
17578             var cs = this.childNodes;
17579             for(var i = 0, len = cs.length; i < len; i++){
17580                 cs[i].renderIndent(true, refresh);
17581             }
17582         }
17583     }
17584 });/*
17585  * Based on:
17586  * Ext JS Library 1.1.1
17587  * Copyright(c) 2006-2007, Ext JS, LLC.
17588  *
17589  * Originally Released Under LGPL - original licence link has changed is not relivant.
17590  *
17591  * Fork - LGPL
17592  * <script type="text/javascript">
17593  */
17594  
17595 /**
17596  * @class Roo.tree.AsyncTreeNode
17597  * @extends Roo.tree.TreeNode
17598  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17599  * @constructor
17600  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17601  */
17602  Roo.tree.AsyncTreeNode = function(config){
17603     this.loaded = false;
17604     this.loading = false;
17605     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17606     /**
17607     * @event beforeload
17608     * Fires before this node is loaded, return false to cancel
17609     * @param {Node} this This node
17610     */
17611     this.addEvents({'beforeload':true, 'load': true});
17612     /**
17613     * @event load
17614     * Fires when this node is loaded
17615     * @param {Node} this This node
17616     */
17617     /**
17618      * The loader used by this node (defaults to using the tree's defined loader)
17619      * @type TreeLoader
17620      * @property loader
17621      */
17622 };
17623 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17624     expand : function(deep, anim, callback){
17625         if(this.loading){ // if an async load is already running, waiting til it's done
17626             var timer;
17627             var f = function(){
17628                 if(!this.loading){ // done loading
17629                     clearInterval(timer);
17630                     this.expand(deep, anim, callback);
17631                 }
17632             }.createDelegate(this);
17633             timer = setInterval(f, 200);
17634             return;
17635         }
17636         if(!this.loaded){
17637             if(this.fireEvent("beforeload", this) === false){
17638                 return;
17639             }
17640             this.loading = true;
17641             this.ui.beforeLoad(this);
17642             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17643             if(loader){
17644                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17645                 return;
17646             }
17647         }
17648         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17649     },
17650     
17651     /**
17652      * Returns true if this node is currently loading
17653      * @return {Boolean}
17654      */
17655     isLoading : function(){
17656         return this.loading;  
17657     },
17658     
17659     loadComplete : function(deep, anim, callback){
17660         this.loading = false;
17661         this.loaded = true;
17662         this.ui.afterLoad(this);
17663         this.fireEvent("load", this);
17664         this.expand(deep, anim, callback);
17665     },
17666     
17667     /**
17668      * Returns true if this node has been loaded
17669      * @return {Boolean}
17670      */
17671     isLoaded : function(){
17672         return this.loaded;
17673     },
17674     
17675     hasChildNodes : function(){
17676         if(!this.isLeaf() && !this.loaded){
17677             return true;
17678         }else{
17679             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17680         }
17681     },
17682
17683     /**
17684      * Trigger a reload for this node
17685      * @param {Function} callback
17686      */
17687     reload : function(callback){
17688         this.collapse(false, false);
17689         while(this.firstChild){
17690             this.removeChild(this.firstChild);
17691         }
17692         this.childrenRendered = false;
17693         this.loaded = false;
17694         if(this.isHiddenRoot()){
17695             this.expanded = false;
17696         }
17697         this.expand(false, false, callback);
17698     }
17699 });/*
17700  * Based on:
17701  * Ext JS Library 1.1.1
17702  * Copyright(c) 2006-2007, Ext JS, LLC.
17703  *
17704  * Originally Released Under LGPL - original licence link has changed is not relivant.
17705  *
17706  * Fork - LGPL
17707  * <script type="text/javascript">
17708  */
17709  
17710 /**
17711  * @class Roo.tree.TreeNodeUI
17712  * @constructor
17713  * @param {Object} node The node to render
17714  * The TreeNode UI implementation is separate from the
17715  * tree implementation. Unless you are customizing the tree UI,
17716  * you should never have to use this directly.
17717  */
17718 Roo.tree.TreeNodeUI = function(node){
17719     this.node = node;
17720     this.rendered = false;
17721     this.animating = false;
17722     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17723 };
17724
17725 Roo.tree.TreeNodeUI.prototype = {
17726     removeChild : function(node){
17727         if(this.rendered){
17728             this.ctNode.removeChild(node.ui.getEl());
17729         }
17730     },
17731
17732     beforeLoad : function(){
17733          this.addClass("x-tree-node-loading");
17734     },
17735
17736     afterLoad : function(){
17737          this.removeClass("x-tree-node-loading");
17738     },
17739
17740     onTextChange : function(node, text, oldText){
17741         if(this.rendered){
17742             this.textNode.innerHTML = text;
17743         }
17744     },
17745
17746     onDisableChange : function(node, state){
17747         this.disabled = state;
17748         if(state){
17749             this.addClass("x-tree-node-disabled");
17750         }else{
17751             this.removeClass("x-tree-node-disabled");
17752         }
17753     },
17754
17755     onSelectedChange : function(state){
17756         if(state){
17757             this.focus();
17758             this.addClass("x-tree-selected");
17759         }else{
17760             //this.blur();
17761             this.removeClass("x-tree-selected");
17762         }
17763     },
17764
17765     onMove : function(tree, node, oldParent, newParent, index, refNode){
17766         this.childIndent = null;
17767         if(this.rendered){
17768             var targetNode = newParent.ui.getContainer();
17769             if(!targetNode){//target not rendered
17770                 this.holder = document.createElement("div");
17771                 this.holder.appendChild(this.wrap);
17772                 return;
17773             }
17774             var insertBefore = refNode ? refNode.ui.getEl() : null;
17775             if(insertBefore){
17776                 targetNode.insertBefore(this.wrap, insertBefore);
17777             }else{
17778                 targetNode.appendChild(this.wrap);
17779             }
17780             this.node.renderIndent(true);
17781         }
17782     },
17783
17784     addClass : function(cls){
17785         if(this.elNode){
17786             Roo.fly(this.elNode).addClass(cls);
17787         }
17788     },
17789
17790     removeClass : function(cls){
17791         if(this.elNode){
17792             Roo.fly(this.elNode).removeClass(cls);
17793         }
17794     },
17795
17796     remove : function(){
17797         if(this.rendered){
17798             this.holder = document.createElement("div");
17799             this.holder.appendChild(this.wrap);
17800         }
17801     },
17802
17803     fireEvent : function(){
17804         return this.node.fireEvent.apply(this.node, arguments);
17805     },
17806
17807     initEvents : function(){
17808         this.node.on("move", this.onMove, this);
17809         var E = Roo.EventManager;
17810         var a = this.anchor;
17811
17812         var el = Roo.fly(a, '_treeui');
17813
17814         if(Roo.isOpera){ // opera render bug ignores the CSS
17815             el.setStyle("text-decoration", "none");
17816         }
17817
17818         el.on("click", this.onClick, this);
17819         el.on("dblclick", this.onDblClick, this);
17820
17821         if(this.checkbox){
17822             Roo.EventManager.on(this.checkbox,
17823                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17824         }
17825
17826         el.on("contextmenu", this.onContextMenu, this);
17827
17828         var icon = Roo.fly(this.iconNode);
17829         icon.on("click", this.onClick, this);
17830         icon.on("dblclick", this.onDblClick, this);
17831         icon.on("contextmenu", this.onContextMenu, this);
17832         E.on(this.ecNode, "click", this.ecClick, this, true);
17833
17834         if(this.node.disabled){
17835             this.addClass("x-tree-node-disabled");
17836         }
17837         if(this.node.hidden){
17838             this.addClass("x-tree-node-disabled");
17839         }
17840         var ot = this.node.getOwnerTree();
17841         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17842         if(dd && (!this.node.isRoot || ot.rootVisible)){
17843             Roo.dd.Registry.register(this.elNode, {
17844                 node: this.node,
17845                 handles: this.getDDHandles(),
17846                 isHandle: false
17847             });
17848         }
17849     },
17850
17851     getDDHandles : function(){
17852         return [this.iconNode, this.textNode];
17853     },
17854
17855     hide : function(){
17856         if(this.rendered){
17857             this.wrap.style.display = "none";
17858         }
17859     },
17860
17861     show : function(){
17862         if(this.rendered){
17863             this.wrap.style.display = "";
17864         }
17865     },
17866
17867     onContextMenu : function(e){
17868         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
17869             e.preventDefault();
17870             this.focus();
17871             this.fireEvent("contextmenu", this.node, e);
17872         }
17873     },
17874
17875     onClick : function(e){
17876         if(this.dropping){
17877             e.stopEvent();
17878             return;
17879         }
17880         if(this.fireEvent("beforeclick", this.node, e) !== false){
17881             if(!this.disabled && this.node.attributes.href){
17882                 this.fireEvent("click", this.node, e);
17883                 return;
17884             }
17885             e.preventDefault();
17886             if(this.disabled){
17887                 return;
17888             }
17889
17890             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
17891                 this.node.toggle();
17892             }
17893
17894             this.fireEvent("click", this.node, e);
17895         }else{
17896             e.stopEvent();
17897         }
17898     },
17899
17900     onDblClick : function(e){
17901         e.preventDefault();
17902         if(this.disabled){
17903             return;
17904         }
17905         if(this.checkbox){
17906             this.toggleCheck();
17907         }
17908         if(!this.animating && this.node.hasChildNodes()){
17909             this.node.toggle();
17910         }
17911         this.fireEvent("dblclick", this.node, e);
17912     },
17913
17914     onCheckChange : function(){
17915         var checked = this.checkbox.checked;
17916         this.node.attributes.checked = checked;
17917         this.fireEvent('checkchange', this.node, checked);
17918     },
17919
17920     ecClick : function(e){
17921         if(!this.animating && this.node.hasChildNodes()){
17922             this.node.toggle();
17923         }
17924     },
17925
17926     startDrop : function(){
17927         this.dropping = true;
17928     },
17929
17930     // delayed drop so the click event doesn't get fired on a drop
17931     endDrop : function(){
17932        setTimeout(function(){
17933            this.dropping = false;
17934        }.createDelegate(this), 50);
17935     },
17936
17937     expand : function(){
17938         this.updateExpandIcon();
17939         this.ctNode.style.display = "";
17940     },
17941
17942     focus : function(){
17943         if(!this.node.preventHScroll){
17944             try{this.anchor.focus();
17945             }catch(e){}
17946         }else if(!Roo.isIE){
17947             try{
17948                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
17949                 var l = noscroll.scrollLeft;
17950                 this.anchor.focus();
17951                 noscroll.scrollLeft = l;
17952             }catch(e){}
17953         }
17954     },
17955
17956     toggleCheck : function(value){
17957         var cb = this.checkbox;
17958         if(cb){
17959             cb.checked = (value === undefined ? !cb.checked : value);
17960         }
17961     },
17962
17963     blur : function(){
17964         try{
17965             this.anchor.blur();
17966         }catch(e){}
17967     },
17968
17969     animExpand : function(callback){
17970         var ct = Roo.get(this.ctNode);
17971         ct.stopFx();
17972         if(!this.node.hasChildNodes()){
17973             this.updateExpandIcon();
17974             this.ctNode.style.display = "";
17975             Roo.callback(callback);
17976             return;
17977         }
17978         this.animating = true;
17979         this.updateExpandIcon();
17980
17981         ct.slideIn('t', {
17982            callback : function(){
17983                this.animating = false;
17984                Roo.callback(callback);
17985             },
17986             scope: this,
17987             duration: this.node.ownerTree.duration || .25
17988         });
17989     },
17990
17991     highlight : function(){
17992         var tree = this.node.getOwnerTree();
17993         Roo.fly(this.wrap).highlight(
17994             tree.hlColor || "C3DAF9",
17995             {endColor: tree.hlBaseColor}
17996         );
17997     },
17998
17999     collapse : function(){
18000         this.updateExpandIcon();
18001         this.ctNode.style.display = "none";
18002     },
18003
18004     animCollapse : function(callback){
18005         var ct = Roo.get(this.ctNode);
18006         ct.enableDisplayMode('block');
18007         ct.stopFx();
18008
18009         this.animating = true;
18010         this.updateExpandIcon();
18011
18012         ct.slideOut('t', {
18013             callback : function(){
18014                this.animating = false;
18015                Roo.callback(callback);
18016             },
18017             scope: this,
18018             duration: this.node.ownerTree.duration || .25
18019         });
18020     },
18021
18022     getContainer : function(){
18023         return this.ctNode;
18024     },
18025
18026     getEl : function(){
18027         return this.wrap;
18028     },
18029
18030     appendDDGhost : function(ghostNode){
18031         ghostNode.appendChild(this.elNode.cloneNode(true));
18032     },
18033
18034     getDDRepairXY : function(){
18035         return Roo.lib.Dom.getXY(this.iconNode);
18036     },
18037
18038     onRender : function(){
18039         this.render();
18040     },
18041
18042     render : function(bulkRender){
18043         var n = this.node, a = n.attributes;
18044         var targetNode = n.parentNode ?
18045               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
18046
18047         if(!this.rendered){
18048             this.rendered = true;
18049
18050             this.renderElements(n, a, targetNode, bulkRender);
18051
18052             if(a.qtip){
18053                if(this.textNode.setAttributeNS){
18054                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
18055                    if(a.qtipTitle){
18056                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
18057                    }
18058                }else{
18059                    this.textNode.setAttribute("ext:qtip", a.qtip);
18060                    if(a.qtipTitle){
18061                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
18062                    }
18063                }
18064             }else if(a.qtipCfg){
18065                 a.qtipCfg.target = Roo.id(this.textNode);
18066                 Roo.QuickTips.register(a.qtipCfg);
18067             }
18068             this.initEvents();
18069             if(!this.node.expanded){
18070                 this.updateExpandIcon();
18071             }
18072         }else{
18073             if(bulkRender === true) {
18074                 targetNode.appendChild(this.wrap);
18075             }
18076         }
18077     },
18078
18079     renderElements : function(n, a, targetNode, bulkRender){
18080         // add some indent caching, this helps performance when rendering a large tree
18081         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18082         var t = n.getOwnerTree();
18083         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18084         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18085         var cb = typeof a.checked == 'boolean';
18086         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18087         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18088             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18089             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18090             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18091             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18092             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18093              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
18094                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18095             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18096             "</li>"];
18097
18098         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18099             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18100                                 n.nextSibling.ui.getEl(), buf.join(""));
18101         }else{
18102             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18103         }
18104
18105         this.elNode = this.wrap.childNodes[0];
18106         this.ctNode = this.wrap.childNodes[1];
18107         var cs = this.elNode.childNodes;
18108         this.indentNode = cs[0];
18109         this.ecNode = cs[1];
18110         this.iconNode = cs[2];
18111         var index = 3;
18112         if(cb){
18113             this.checkbox = cs[3];
18114             index++;
18115         }
18116         this.anchor = cs[index];
18117         this.textNode = cs[index].firstChild;
18118     },
18119
18120     getAnchor : function(){
18121         return this.anchor;
18122     },
18123
18124     getTextEl : function(){
18125         return this.textNode;
18126     },
18127
18128     getIconEl : function(){
18129         return this.iconNode;
18130     },
18131
18132     isChecked : function(){
18133         return this.checkbox ? this.checkbox.checked : false;
18134     },
18135
18136     updateExpandIcon : function(){
18137         if(this.rendered){
18138             var n = this.node, c1, c2;
18139             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18140             var hasChild = n.hasChildNodes();
18141             if(hasChild){
18142                 if(n.expanded){
18143                     cls += "-minus";
18144                     c1 = "x-tree-node-collapsed";
18145                     c2 = "x-tree-node-expanded";
18146                 }else{
18147                     cls += "-plus";
18148                     c1 = "x-tree-node-expanded";
18149                     c2 = "x-tree-node-collapsed";
18150                 }
18151                 if(this.wasLeaf){
18152                     this.removeClass("x-tree-node-leaf");
18153                     this.wasLeaf = false;
18154                 }
18155                 if(this.c1 != c1 || this.c2 != c2){
18156                     Roo.fly(this.elNode).replaceClass(c1, c2);
18157                     this.c1 = c1; this.c2 = c2;
18158                 }
18159             }else{
18160                 if(!this.wasLeaf){
18161                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18162                     delete this.c1;
18163                     delete this.c2;
18164                     this.wasLeaf = true;
18165                 }
18166             }
18167             var ecc = "x-tree-ec-icon "+cls;
18168             if(this.ecc != ecc){
18169                 this.ecNode.className = ecc;
18170                 this.ecc = ecc;
18171             }
18172         }
18173     },
18174
18175     getChildIndent : function(){
18176         if(!this.childIndent){
18177             var buf = [];
18178             var p = this.node;
18179             while(p){
18180                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18181                     if(!p.isLast()) {
18182                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18183                     } else {
18184                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18185                     }
18186                 }
18187                 p = p.parentNode;
18188             }
18189             this.childIndent = buf.join("");
18190         }
18191         return this.childIndent;
18192     },
18193
18194     renderIndent : function(){
18195         if(this.rendered){
18196             var indent = "";
18197             var p = this.node.parentNode;
18198             if(p){
18199                 indent = p.ui.getChildIndent();
18200             }
18201             if(this.indentMarkup != indent){ // don't rerender if not required
18202                 this.indentNode.innerHTML = indent;
18203                 this.indentMarkup = indent;
18204             }
18205             this.updateExpandIcon();
18206         }
18207     }
18208 };
18209
18210 Roo.tree.RootTreeNodeUI = function(){
18211     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18212 };
18213 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18214     render : function(){
18215         if(!this.rendered){
18216             var targetNode = this.node.ownerTree.innerCt.dom;
18217             this.node.expanded = true;
18218             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18219             this.wrap = this.ctNode = targetNode.firstChild;
18220         }
18221     },
18222     collapse : function(){
18223     },
18224     expand : function(){
18225     }
18226 });/*
18227  * Based on:
18228  * Ext JS Library 1.1.1
18229  * Copyright(c) 2006-2007, Ext JS, LLC.
18230  *
18231  * Originally Released Under LGPL - original licence link has changed is not relivant.
18232  *
18233  * Fork - LGPL
18234  * <script type="text/javascript">
18235  */
18236 /**
18237  * @class Roo.tree.TreeLoader
18238  * @extends Roo.util.Observable
18239  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18240  * nodes from a specified URL. The response must be a javascript Array definition
18241  * who's elements are node definition objects. eg:
18242  * <pre><code>
18243    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
18244     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
18245 </code></pre>
18246  * <br><br>
18247  * A server request is sent, and child nodes are loaded only when a node is expanded.
18248  * The loading node's id is passed to the server under the parameter name "node" to
18249  * enable the server to produce the correct child nodes.
18250  * <br><br>
18251  * To pass extra parameters, an event handler may be attached to the "beforeload"
18252  * event, and the parameters specified in the TreeLoader's baseParams property:
18253  * <pre><code>
18254     myTreeLoader.on("beforeload", function(treeLoader, node) {
18255         this.baseParams.category = node.attributes.category;
18256     }, this);
18257 </code></pre><
18258  * This would pass an HTTP parameter called "category" to the server containing
18259  * the value of the Node's "category" attribute.
18260  * @constructor
18261  * Creates a new Treeloader.
18262  * @param {Object} config A config object containing config properties.
18263  */
18264 Roo.tree.TreeLoader = function(config){
18265     this.baseParams = {};
18266     this.requestMethod = "POST";
18267     Roo.apply(this, config);
18268
18269     this.addEvents({
18270     
18271         /**
18272          * @event beforeload
18273          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18274          * @param {Object} This TreeLoader object.
18275          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18276          * @param {Object} callback The callback function specified in the {@link #load} call.
18277          */
18278         beforeload : true,
18279         /**
18280          * @event load
18281          * Fires when the node has been successfuly loaded.
18282          * @param {Object} This TreeLoader object.
18283          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18284          * @param {Object} response The response object containing the data from the server.
18285          */
18286         load : true,
18287         /**
18288          * @event loadexception
18289          * Fires if the network request failed.
18290          * @param {Object} This TreeLoader object.
18291          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18292          * @param {Object} response The response object containing the data from the server.
18293          */
18294         loadexception : true,
18295         /**
18296          * @event create
18297          * Fires before a node is created, enabling you to return custom Node types 
18298          * @param {Object} This TreeLoader object.
18299          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18300          */
18301         create : true
18302     });
18303
18304     Roo.tree.TreeLoader.superclass.constructor.call(this);
18305 };
18306
18307 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18308     /**
18309     * @cfg {String} dataUrl The URL from which to request a Json string which
18310     * specifies an array of node definition object representing the child nodes
18311     * to be loaded.
18312     */
18313     /**
18314     * @cfg {Object} baseParams (optional) An object containing properties which
18315     * specify HTTP parameters to be passed to each request for child nodes.
18316     */
18317     /**
18318     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18319     * created by this loader. If the attributes sent by the server have an attribute in this object,
18320     * they take priority.
18321     */
18322     /**
18323     * @cfg {Object} uiProviders (optional) An object containing properties which
18324     * 
18325     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
18326     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18327     * <i>uiProvider</i> attribute of a returned child node is a string rather
18328     * than a reference to a TreeNodeUI implementation, this that string value
18329     * is used as a property name in the uiProviders object. You can define the provider named
18330     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18331     */
18332     uiProviders : {},
18333
18334     /**
18335     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18336     * child nodes before loading.
18337     */
18338     clearOnLoad : true,
18339
18340     /**
18341     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18342     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18343     * Grid query { data : [ .....] }
18344     */
18345     
18346     root : false,
18347      /**
18348     * @cfg {String} queryParam (optional) 
18349     * Name of the query as it will be passed on the querystring (defaults to 'node')
18350     * eg. the request will be ?node=[id]
18351     */
18352     
18353     
18354     queryParam: false,
18355     
18356     /**
18357      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18358      * This is called automatically when a node is expanded, but may be used to reload
18359      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18360      * @param {Roo.tree.TreeNode} node
18361      * @param {Function} callback
18362      */
18363     load : function(node, callback){
18364         if(this.clearOnLoad){
18365             while(node.firstChild){
18366                 node.removeChild(node.firstChild);
18367             }
18368         }
18369         if(node.attributes.children){ // preloaded json children
18370             var cs = node.attributes.children;
18371             for(var i = 0, len = cs.length; i < len; i++){
18372                 node.appendChild(this.createNode(cs[i]));
18373             }
18374             if(typeof callback == "function"){
18375                 callback();
18376             }
18377         }else if(this.dataUrl){
18378             this.requestData(node, callback);
18379         }
18380     },
18381
18382     getParams: function(node){
18383         var buf = [], bp = this.baseParams;
18384         for(var key in bp){
18385             if(typeof bp[key] != "function"){
18386                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18387             }
18388         }
18389         var n = this.queryParam === false ? 'node' : this.queryParam;
18390         buf.push(n + "=", encodeURIComponent(node.id));
18391         return buf.join("");
18392     },
18393
18394     requestData : function(node, callback){
18395         if(this.fireEvent("beforeload", this, node, callback) !== false){
18396             this.transId = Roo.Ajax.request({
18397                 method:this.requestMethod,
18398                 url: this.dataUrl||this.url,
18399                 success: this.handleResponse,
18400                 failure: this.handleFailure,
18401                 scope: this,
18402                 argument: {callback: callback, node: node},
18403                 params: this.getParams(node)
18404             });
18405         }else{
18406             // if the load is cancelled, make sure we notify
18407             // the node that we are done
18408             if(typeof callback == "function"){
18409                 callback();
18410             }
18411         }
18412     },
18413
18414     isLoading : function(){
18415         return this.transId ? true : false;
18416     },
18417
18418     abort : function(){
18419         if(this.isLoading()){
18420             Roo.Ajax.abort(this.transId);
18421         }
18422     },
18423
18424     // private
18425     createNode : function(attr){
18426         // apply baseAttrs, nice idea Corey!
18427         if(this.baseAttrs){
18428             Roo.applyIf(attr, this.baseAttrs);
18429         }
18430         if(this.applyLoader !== false){
18431             attr.loader = this;
18432         }
18433         // uiProvider = depreciated..
18434         
18435         if(typeof(attr.uiProvider) == 'string'){
18436            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18437                 /**  eval:var:attr */ eval(attr.uiProvider);
18438         }
18439         if(typeof(this.uiProviders['default']) != 'undefined') {
18440             attr.uiProvider = this.uiProviders['default'];
18441         }
18442         
18443         this.fireEvent('create', this, attr);
18444         
18445         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18446         return(attr.leaf ?
18447                         new Roo.tree.TreeNode(attr) :
18448                         new Roo.tree.AsyncTreeNode(attr));
18449     },
18450
18451     processResponse : function(response, node, callback){
18452         var json = response.responseText;
18453         try {
18454             
18455             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
18456             if (this.root !== false) {
18457                 o = o[this.root];
18458             }
18459             
18460             for(var i = 0, len = o.length; i < len; i++){
18461                 var n = this.createNode(o[i]);
18462                 if(n){
18463                     node.appendChild(n);
18464                 }
18465             }
18466             if(typeof callback == "function"){
18467                 callback(this, node);
18468             }
18469         }catch(e){
18470             this.handleFailure(response);
18471         }
18472     },
18473
18474     handleResponse : function(response){
18475         this.transId = false;
18476         var a = response.argument;
18477         this.processResponse(response, a.node, a.callback);
18478         this.fireEvent("load", this, a.node, response);
18479     },
18480
18481     handleFailure : function(response){
18482         this.transId = false;
18483         var a = response.argument;
18484         this.fireEvent("loadexception", this, a.node, response);
18485         if(typeof a.callback == "function"){
18486             a.callback(this, a.node);
18487         }
18488     }
18489 });/*
18490  * Based on:
18491  * Ext JS Library 1.1.1
18492  * Copyright(c) 2006-2007, Ext JS, LLC.
18493  *
18494  * Originally Released Under LGPL - original licence link has changed is not relivant.
18495  *
18496  * Fork - LGPL
18497  * <script type="text/javascript">
18498  */
18499
18500 /**
18501 * @class Roo.tree.TreeFilter
18502 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18503 * @param {TreePanel} tree
18504 * @param {Object} config (optional)
18505  */
18506 Roo.tree.TreeFilter = function(tree, config){
18507     this.tree = tree;
18508     this.filtered = {};
18509     Roo.apply(this, config);
18510 };
18511
18512 Roo.tree.TreeFilter.prototype = {
18513     clearBlank:false,
18514     reverse:false,
18515     autoClear:false,
18516     remove:false,
18517
18518      /**
18519      * Filter the data by a specific attribute.
18520      * @param {String/RegExp} value Either string that the attribute value
18521      * should start with or a RegExp to test against the attribute
18522      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18523      * @param {TreeNode} startNode (optional) The node to start the filter at.
18524      */
18525     filter : function(value, attr, startNode){
18526         attr = attr || "text";
18527         var f;
18528         if(typeof value == "string"){
18529             var vlen = value.length;
18530             // auto clear empty filter
18531             if(vlen == 0 && this.clearBlank){
18532                 this.clear();
18533                 return;
18534             }
18535             value = value.toLowerCase();
18536             f = function(n){
18537                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18538             };
18539         }else if(value.exec){ // regex?
18540             f = function(n){
18541                 return value.test(n.attributes[attr]);
18542             };
18543         }else{
18544             throw 'Illegal filter type, must be string or regex';
18545         }
18546         this.filterBy(f, null, startNode);
18547         },
18548
18549     /**
18550      * Filter by a function. The passed function will be called with each
18551      * node in the tree (or from the startNode). If the function returns true, the node is kept
18552      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18553      * @param {Function} fn The filter function
18554      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18555      */
18556     filterBy : function(fn, scope, startNode){
18557         startNode = startNode || this.tree.root;
18558         if(this.autoClear){
18559             this.clear();
18560         }
18561         var af = this.filtered, rv = this.reverse;
18562         var f = function(n){
18563             if(n == startNode){
18564                 return true;
18565             }
18566             if(af[n.id]){
18567                 return false;
18568             }
18569             var m = fn.call(scope || n, n);
18570             if(!m || rv){
18571                 af[n.id] = n;
18572                 n.ui.hide();
18573                 return false;
18574             }
18575             return true;
18576         };
18577         startNode.cascade(f);
18578         if(this.remove){
18579            for(var id in af){
18580                if(typeof id != "function"){
18581                    var n = af[id];
18582                    if(n && n.parentNode){
18583                        n.parentNode.removeChild(n);
18584                    }
18585                }
18586            }
18587         }
18588     },
18589
18590     /**
18591      * Clears the current filter. Note: with the "remove" option
18592      * set a filter cannot be cleared.
18593      */
18594     clear : function(){
18595         var t = this.tree;
18596         var af = this.filtered;
18597         for(var id in af){
18598             if(typeof id != "function"){
18599                 var n = af[id];
18600                 if(n){
18601                     n.ui.show();
18602                 }
18603             }
18604         }
18605         this.filtered = {};
18606     }
18607 };
18608 /*
18609  * Based on:
18610  * Ext JS Library 1.1.1
18611  * Copyright(c) 2006-2007, Ext JS, LLC.
18612  *
18613  * Originally Released Under LGPL - original licence link has changed is not relivant.
18614  *
18615  * Fork - LGPL
18616  * <script type="text/javascript">
18617  */
18618  
18619
18620 /**
18621  * @class Roo.tree.TreeSorter
18622  * Provides sorting of nodes in a TreePanel
18623  * 
18624  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18625  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18626  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18627  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18628  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18629  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18630  * @constructor
18631  * @param {TreePanel} tree
18632  * @param {Object} config
18633  */
18634 Roo.tree.TreeSorter = function(tree, config){
18635     Roo.apply(this, config);
18636     tree.on("beforechildrenrendered", this.doSort, this);
18637     tree.on("append", this.updateSort, this);
18638     tree.on("insert", this.updateSort, this);
18639     
18640     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18641     var p = this.property || "text";
18642     var sortType = this.sortType;
18643     var fs = this.folderSort;
18644     var cs = this.caseSensitive === true;
18645     var leafAttr = this.leafAttr || 'leaf';
18646
18647     this.sortFn = function(n1, n2){
18648         if(fs){
18649             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18650                 return 1;
18651             }
18652             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18653                 return -1;
18654             }
18655         }
18656         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18657         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18658         if(v1 < v2){
18659                         return dsc ? +1 : -1;
18660                 }else if(v1 > v2){
18661                         return dsc ? -1 : +1;
18662         }else{
18663                 return 0;
18664         }
18665     };
18666 };
18667
18668 Roo.tree.TreeSorter.prototype = {
18669     doSort : function(node){
18670         node.sort(this.sortFn);
18671     },
18672     
18673     compareNodes : function(n1, n2){
18674         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18675     },
18676     
18677     updateSort : function(tree, node){
18678         if(node.childrenRendered){
18679             this.doSort.defer(1, this, [node]);
18680         }
18681     }
18682 };/*
18683  * Based on:
18684  * Ext JS Library 1.1.1
18685  * Copyright(c) 2006-2007, Ext JS, LLC.
18686  *
18687  * Originally Released Under LGPL - original licence link has changed is not relivant.
18688  *
18689  * Fork - LGPL
18690  * <script type="text/javascript">
18691  */
18692
18693 if(Roo.dd.DropZone){
18694     
18695 Roo.tree.TreeDropZone = function(tree, config){
18696     this.allowParentInsert = false;
18697     this.allowContainerDrop = false;
18698     this.appendOnly = false;
18699     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18700     this.tree = tree;
18701     this.lastInsertClass = "x-tree-no-status";
18702     this.dragOverData = {};
18703 };
18704
18705 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18706     ddGroup : "TreeDD",
18707     
18708     expandDelay : 1000,
18709     
18710     expandNode : function(node){
18711         if(node.hasChildNodes() && !node.isExpanded()){
18712             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18713         }
18714     },
18715     
18716     queueExpand : function(node){
18717         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18718     },
18719     
18720     cancelExpand : function(){
18721         if(this.expandProcId){
18722             clearTimeout(this.expandProcId);
18723             this.expandProcId = false;
18724         }
18725     },
18726     
18727     isValidDropPoint : function(n, pt, dd, e, data){
18728         if(!n || !data){ return false; }
18729         var targetNode = n.node;
18730         var dropNode = data.node;
18731         // default drop rules
18732         if(!(targetNode && targetNode.isTarget && pt)){
18733             return false;
18734         }
18735         if(pt == "append" && targetNode.allowChildren === false){
18736             return false;
18737         }
18738         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18739             return false;
18740         }
18741         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18742             return false;
18743         }
18744         // reuse the object
18745         var overEvent = this.dragOverData;
18746         overEvent.tree = this.tree;
18747         overEvent.target = targetNode;
18748         overEvent.data = data;
18749         overEvent.point = pt;
18750         overEvent.source = dd;
18751         overEvent.rawEvent = e;
18752         overEvent.dropNode = dropNode;
18753         overEvent.cancel = false;  
18754         var result = this.tree.fireEvent("nodedragover", overEvent);
18755         return overEvent.cancel === false && result !== false;
18756     },
18757     
18758     getDropPoint : function(e, n, dd){
18759         var tn = n.node;
18760         if(tn.isRoot){
18761             return tn.allowChildren !== false ? "append" : false; // always append for root
18762         }
18763         var dragEl = n.ddel;
18764         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18765         var y = Roo.lib.Event.getPageY(e);
18766         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18767         
18768         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18769         var noAppend = tn.allowChildren === false;
18770         if(this.appendOnly || tn.parentNode.allowChildren === false){
18771             return noAppend ? false : "append";
18772         }
18773         var noBelow = false;
18774         if(!this.allowParentInsert){
18775             noBelow = tn.hasChildNodes() && tn.isExpanded();
18776         }
18777         var q = (b - t) / (noAppend ? 2 : 3);
18778         if(y >= t && y < (t + q)){
18779             return "above";
18780         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18781             return "below";
18782         }else{
18783             return "append";
18784         }
18785     },
18786     
18787     onNodeEnter : function(n, dd, e, data){
18788         this.cancelExpand();
18789     },
18790     
18791     onNodeOver : function(n, dd, e, data){
18792         var pt = this.getDropPoint(e, n, dd);
18793         var node = n.node;
18794         
18795         // auto node expand check
18796         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18797             this.queueExpand(node);
18798         }else if(pt != "append"){
18799             this.cancelExpand();
18800         }
18801         
18802         // set the insert point style on the target node
18803         var returnCls = this.dropNotAllowed;
18804         if(this.isValidDropPoint(n, pt, dd, e, data)){
18805            if(pt){
18806                var el = n.ddel;
18807                var cls;
18808                if(pt == "above"){
18809                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18810                    cls = "x-tree-drag-insert-above";
18811                }else if(pt == "below"){
18812                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18813                    cls = "x-tree-drag-insert-below";
18814                }else{
18815                    returnCls = "x-tree-drop-ok-append";
18816                    cls = "x-tree-drag-append";
18817                }
18818                if(this.lastInsertClass != cls){
18819                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18820                    this.lastInsertClass = cls;
18821                }
18822            }
18823        }
18824        return returnCls;
18825     },
18826     
18827     onNodeOut : function(n, dd, e, data){
18828         this.cancelExpand();
18829         this.removeDropIndicators(n);
18830     },
18831     
18832     onNodeDrop : function(n, dd, e, data){
18833         var point = this.getDropPoint(e, n, dd);
18834         var targetNode = n.node;
18835         targetNode.ui.startDrop();
18836         if(!this.isValidDropPoint(n, point, dd, e, data)){
18837             targetNode.ui.endDrop();
18838             return false;
18839         }
18840         // first try to find the drop node
18841         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
18842         var dropEvent = {
18843             tree : this.tree,
18844             target: targetNode,
18845             data: data,
18846             point: point,
18847             source: dd,
18848             rawEvent: e,
18849             dropNode: dropNode,
18850             cancel: !dropNode   
18851         };
18852         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
18853         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
18854             targetNode.ui.endDrop();
18855             return false;
18856         }
18857         // allow target changing
18858         targetNode = dropEvent.target;
18859         if(point == "append" && !targetNode.isExpanded()){
18860             targetNode.expand(false, null, function(){
18861                 this.completeDrop(dropEvent);
18862             }.createDelegate(this));
18863         }else{
18864             this.completeDrop(dropEvent);
18865         }
18866         return true;
18867     },
18868     
18869     completeDrop : function(de){
18870         var ns = de.dropNode, p = de.point, t = de.target;
18871         if(!(ns instanceof Array)){
18872             ns = [ns];
18873         }
18874         var n;
18875         for(var i = 0, len = ns.length; i < len; i++){
18876             n = ns[i];
18877             if(p == "above"){
18878                 t.parentNode.insertBefore(n, t);
18879             }else if(p == "below"){
18880                 t.parentNode.insertBefore(n, t.nextSibling);
18881             }else{
18882                 t.appendChild(n);
18883             }
18884         }
18885         n.ui.focus();
18886         if(this.tree.hlDrop){
18887             n.ui.highlight();
18888         }
18889         t.ui.endDrop();
18890         this.tree.fireEvent("nodedrop", de);
18891     },
18892     
18893     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
18894         if(this.tree.hlDrop){
18895             dropNode.ui.focus();
18896             dropNode.ui.highlight();
18897         }
18898         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
18899     },
18900     
18901     getTree : function(){
18902         return this.tree;
18903     },
18904     
18905     removeDropIndicators : function(n){
18906         if(n && n.ddel){
18907             var el = n.ddel;
18908             Roo.fly(el).removeClass([
18909                     "x-tree-drag-insert-above",
18910                     "x-tree-drag-insert-below",
18911                     "x-tree-drag-append"]);
18912             this.lastInsertClass = "_noclass";
18913         }
18914     },
18915     
18916     beforeDragDrop : function(target, e, id){
18917         this.cancelExpand();
18918         return true;
18919     },
18920     
18921     afterRepair : function(data){
18922         if(data && Roo.enableFx){
18923             data.node.ui.highlight();
18924         }
18925         this.hideProxy();
18926     }    
18927 });
18928
18929 }
18930 /*
18931  * Based on:
18932  * Ext JS Library 1.1.1
18933  * Copyright(c) 2006-2007, Ext JS, LLC.
18934  *
18935  * Originally Released Under LGPL - original licence link has changed is not relivant.
18936  *
18937  * Fork - LGPL
18938  * <script type="text/javascript">
18939  */
18940  
18941
18942 if(Roo.dd.DragZone){
18943 Roo.tree.TreeDragZone = function(tree, config){
18944     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
18945     this.tree = tree;
18946 };
18947
18948 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
18949     ddGroup : "TreeDD",
18950     
18951     onBeforeDrag : function(data, e){
18952         var n = data.node;
18953         return n && n.draggable && !n.disabled;
18954     },
18955     
18956     onInitDrag : function(e){
18957         var data = this.dragData;
18958         this.tree.getSelectionModel().select(data.node);
18959         this.proxy.update("");
18960         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
18961         this.tree.fireEvent("startdrag", this.tree, data.node, e);
18962     },
18963     
18964     getRepairXY : function(e, data){
18965         return data.node.ui.getDDRepairXY();
18966     },
18967     
18968     onEndDrag : function(data, e){
18969         this.tree.fireEvent("enddrag", this.tree, data.node, e);
18970     },
18971     
18972     onValidDrop : function(dd, e, id){
18973         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
18974         this.hideProxy();
18975     },
18976     
18977     beforeInvalidDrop : function(e, id){
18978         // this scrolls the original position back into view
18979         var sm = this.tree.getSelectionModel();
18980         sm.clearSelections();
18981         sm.select(this.dragData.node);
18982     }
18983 });
18984 }/*
18985  * Based on:
18986  * Ext JS Library 1.1.1
18987  * Copyright(c) 2006-2007, Ext JS, LLC.
18988  *
18989  * Originally Released Under LGPL - original licence link has changed is not relivant.
18990  *
18991  * Fork - LGPL
18992  * <script type="text/javascript">
18993  */
18994 /**
18995  * @class Roo.tree.TreeEditor
18996  * @extends Roo.Editor
18997  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
18998  * as the editor field.
18999  * @constructor
19000  * @param {TreePanel} tree
19001  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
19002  */
19003 Roo.tree.TreeEditor = function(tree, config){
19004     config = config || {};
19005     var field = config.events ? config : new Roo.form.TextField(config);
19006     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
19007
19008     this.tree = tree;
19009
19010     tree.on('beforeclick', this.beforeNodeClick, this);
19011     tree.getTreeEl().on('mousedown', this.hide, this);
19012     this.on('complete', this.updateNode, this);
19013     this.on('beforestartedit', this.fitToTree, this);
19014     this.on('startedit', this.bindScroll, this, {delay:10});
19015     this.on('specialkey', this.onSpecialKey, this);
19016 };
19017
19018 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
19019     /**
19020      * @cfg {String} alignment
19021      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
19022      */
19023     alignment: "l-l",
19024     // inherit
19025     autoSize: false,
19026     /**
19027      * @cfg {Boolean} hideEl
19028      * True to hide the bound element while the editor is displayed (defaults to false)
19029      */
19030     hideEl : false,
19031     /**
19032      * @cfg {String} cls
19033      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
19034      */
19035     cls: "x-small-editor x-tree-editor",
19036     /**
19037      * @cfg {Boolean} shim
19038      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
19039      */
19040     shim:false,
19041     // inherit
19042     shadow:"frame",
19043     /**
19044      * @cfg {Number} maxWidth
19045      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
19046      * the containing tree element's size, it will be automatically limited for you to the container width, taking
19047      * scroll and client offsets into account prior to each edit.
19048      */
19049     maxWidth: 250,
19050
19051     editDelay : 350,
19052
19053     // private
19054     fitToTree : function(ed, el){
19055         var td = this.tree.getTreeEl().dom, nd = el.dom;
19056         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
19057             td.scrollLeft = nd.offsetLeft;
19058         }
19059         var w = Math.min(
19060                 this.maxWidth,
19061                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
19062         this.setSize(w, '');
19063     },
19064
19065     // private
19066     triggerEdit : function(node){
19067         this.completeEdit();
19068         this.editNode = node;
19069         this.startEdit(node.ui.textNode, node.text);
19070     },
19071
19072     // private
19073     bindScroll : function(){
19074         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
19075     },
19076
19077     // private
19078     beforeNodeClick : function(node, e){
19079         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19080         this.lastClick = new Date();
19081         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19082             e.stopEvent();
19083             this.triggerEdit(node);
19084             return false;
19085         }
19086     },
19087
19088     // private
19089     updateNode : function(ed, value){
19090         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19091         this.editNode.setText(value);
19092     },
19093
19094     // private
19095     onHide : function(){
19096         Roo.tree.TreeEditor.superclass.onHide.call(this);
19097         if(this.editNode){
19098             this.editNode.ui.focus();
19099         }
19100     },
19101
19102     // private
19103     onSpecialKey : function(field, e){
19104         var k = e.getKey();
19105         if(k == e.ESC){
19106             e.stopEvent();
19107             this.cancelEdit();
19108         }else if(k == e.ENTER && !e.hasModifier()){
19109             e.stopEvent();
19110             this.completeEdit();
19111         }
19112     }
19113 });//<Script type="text/javascript">
19114 /*
19115  * Based on:
19116  * Ext JS Library 1.1.1
19117  * Copyright(c) 2006-2007, Ext JS, LLC.
19118  *
19119  * Originally Released Under LGPL - original licence link has changed is not relivant.
19120  *
19121  * Fork - LGPL
19122  * <script type="text/javascript">
19123  */
19124  
19125 /**
19126  * Not documented??? - probably should be...
19127  */
19128
19129 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19130     //focus: Roo.emptyFn, // prevent odd scrolling behavior
19131     
19132     renderElements : function(n, a, targetNode, bulkRender){
19133         //consel.log("renderElements?");
19134         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19135
19136         var t = n.getOwnerTree();
19137         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19138         
19139         var cols = t.columns;
19140         var bw = t.borderWidth;
19141         var c = cols[0];
19142         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19143          var cb = typeof a.checked == "boolean";
19144         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19145         var colcls = 'x-t-' + tid + '-c0';
19146         var buf = [
19147             '<li class="x-tree-node">',
19148             
19149                 
19150                 '<div class="x-tree-node-el ', a.cls,'">',
19151                     // extran...
19152                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19153                 
19154                 
19155                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19156                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
19157                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19158                            (a.icon ? ' x-tree-node-inline-icon' : ''),
19159                            (a.iconCls ? ' '+a.iconCls : ''),
19160                            '" unselectable="on" />',
19161                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
19162                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
19163                              
19164                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19165                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19166                             '<span unselectable="on" qtip="' + tx + '">',
19167                              tx,
19168                              '</span></a>' ,
19169                     '</div>',
19170                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19171                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19172                  ];
19173         for(var i = 1, len = cols.length; i < len; i++){
19174             c = cols[i];
19175             colcls = 'x-t-' + tid + '-c' +i;
19176             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19177             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19178                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19179                       "</div>");
19180          }
19181          
19182          buf.push(
19183             '</a>',
19184             '<div class="x-clear"></div></div>',
19185             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19186             "</li>");
19187         
19188         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19189             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19190                                 n.nextSibling.ui.getEl(), buf.join(""));
19191         }else{
19192             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19193         }
19194         var el = this.wrap.firstChild;
19195         this.elRow = el;
19196         this.elNode = el.firstChild;
19197         this.ranchor = el.childNodes[1];
19198         this.ctNode = this.wrap.childNodes[1];
19199         var cs = el.firstChild.childNodes;
19200         this.indentNode = cs[0];
19201         this.ecNode = cs[1];
19202         this.iconNode = cs[2];
19203         var index = 3;
19204         if(cb){
19205             this.checkbox = cs[3];
19206             index++;
19207         }
19208         this.anchor = cs[index];
19209         
19210         this.textNode = cs[index].firstChild;
19211         
19212         //el.on("click", this.onClick, this);
19213         //el.on("dblclick", this.onDblClick, this);
19214         
19215         
19216        // console.log(this);
19217     },
19218     initEvents : function(){
19219         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19220         
19221             
19222         var a = this.ranchor;
19223
19224         var el = Roo.get(a);
19225
19226         if(Roo.isOpera){ // opera render bug ignores the CSS
19227             el.setStyle("text-decoration", "none");
19228         }
19229
19230         el.on("click", this.onClick, this);
19231         el.on("dblclick", this.onDblClick, this);
19232         el.on("contextmenu", this.onContextMenu, this);
19233         
19234     },
19235     
19236     /*onSelectedChange : function(state){
19237         if(state){
19238             this.focus();
19239             this.addClass("x-tree-selected");
19240         }else{
19241             //this.blur();
19242             this.removeClass("x-tree-selected");
19243         }
19244     },*/
19245     addClass : function(cls){
19246         if(this.elRow){
19247             Roo.fly(this.elRow).addClass(cls);
19248         }
19249         
19250     },
19251     
19252     
19253     removeClass : function(cls){
19254         if(this.elRow){
19255             Roo.fly(this.elRow).removeClass(cls);
19256         }
19257     }
19258
19259     
19260     
19261 });//<Script type="text/javascript">
19262
19263 /*
19264  * Based on:
19265  * Ext JS Library 1.1.1
19266  * Copyright(c) 2006-2007, Ext JS, LLC.
19267  *
19268  * Originally Released Under LGPL - original licence link has changed is not relivant.
19269  *
19270  * Fork - LGPL
19271  * <script type="text/javascript">
19272  */
19273  
19274
19275 /**
19276  * @class Roo.tree.ColumnTree
19277  * @extends Roo.data.TreePanel
19278  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19279  * @cfg {int} borderWidth  compined right/left border allowance
19280  * @constructor
19281  * @param {String/HTMLElement/Element} el The container element
19282  * @param {Object} config
19283  */
19284 Roo.tree.ColumnTree =  function(el, config)
19285 {
19286    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19287    this.addEvents({
19288         /**
19289         * @event resize
19290         * Fire this event on a container when it resizes
19291         * @param {int} w Width
19292         * @param {int} h Height
19293         */
19294        "resize" : true
19295     });
19296     this.on('resize', this.onResize, this);
19297 };
19298
19299 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19300     //lines:false,
19301     
19302     
19303     borderWidth: Roo.isBorderBox ? 0 : 2, 
19304     headEls : false,
19305     
19306     render : function(){
19307         // add the header.....
19308        
19309         Roo.tree.ColumnTree.superclass.render.apply(this);
19310         
19311         this.el.addClass('x-column-tree');
19312         
19313         this.headers = this.el.createChild(
19314             {cls:'x-tree-headers'},this.innerCt.dom);
19315    
19316         var cols = this.columns, c;
19317         var totalWidth = 0;
19318         this.headEls = [];
19319         var  len = cols.length;
19320         for(var i = 0; i < len; i++){
19321              c = cols[i];
19322              totalWidth += c.width;
19323             this.headEls.push(this.headers.createChild({
19324                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19325                  cn: {
19326                      cls:'x-tree-hd-text',
19327                      html: c.header
19328                  },
19329                  style:'width:'+(c.width-this.borderWidth)+'px;'
19330              }));
19331         }
19332         this.headers.createChild({cls:'x-clear'});
19333         // prevent floats from wrapping when clipped
19334         this.headers.setWidth(totalWidth);
19335         //this.innerCt.setWidth(totalWidth);
19336         this.innerCt.setStyle({ overflow: 'auto' });
19337         this.onResize(this.width, this.height);
19338              
19339         
19340     },
19341     onResize : function(w,h)
19342     {
19343         this.height = h;
19344         this.width = w;
19345         // resize cols..
19346         this.innerCt.setWidth(this.width);
19347         this.innerCt.setHeight(this.height-20);
19348         
19349         // headers...
19350         var cols = this.columns, c;
19351         var totalWidth = 0;
19352         var expEl = false;
19353         var len = cols.length;
19354         for(var i = 0; i < len; i++){
19355             c = cols[i];
19356             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19357                 // it's the expander..
19358                 expEl  = this.headEls[i];
19359                 continue;
19360             }
19361             totalWidth += c.width;
19362             
19363         }
19364         if (expEl) {
19365             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19366         }
19367         this.headers.setWidth(w-20);
19368
19369         
19370         
19371         
19372     }
19373 });
19374 /*
19375  * Based on:
19376  * Ext JS Library 1.1.1
19377  * Copyright(c) 2006-2007, Ext JS, LLC.
19378  *
19379  * Originally Released Under LGPL - original licence link has changed is not relivant.
19380  *
19381  * Fork - LGPL
19382  * <script type="text/javascript">
19383  */
19384  
19385 /**
19386  * @class Roo.menu.Menu
19387  * @extends Roo.util.Observable
19388  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19389  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19390  * @constructor
19391  * Creates a new Menu
19392  * @param {Object} config Configuration options
19393  */
19394 Roo.menu.Menu = function(config){
19395     Roo.apply(this, config);
19396     this.id = this.id || Roo.id();
19397     this.addEvents({
19398         /**
19399          * @event beforeshow
19400          * Fires before this menu is displayed
19401          * @param {Roo.menu.Menu} this
19402          */
19403         beforeshow : true,
19404         /**
19405          * @event beforehide
19406          * Fires before this menu is hidden
19407          * @param {Roo.menu.Menu} this
19408          */
19409         beforehide : true,
19410         /**
19411          * @event show
19412          * Fires after this menu is displayed
19413          * @param {Roo.menu.Menu} this
19414          */
19415         show : true,
19416         /**
19417          * @event hide
19418          * Fires after this menu is hidden
19419          * @param {Roo.menu.Menu} this
19420          */
19421         hide : true,
19422         /**
19423          * @event click
19424          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19425          * @param {Roo.menu.Menu} this
19426          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19427          * @param {Roo.EventObject} e
19428          */
19429         click : true,
19430         /**
19431          * @event mouseover
19432          * Fires when the mouse is hovering over this menu
19433          * @param {Roo.menu.Menu} this
19434          * @param {Roo.EventObject} e
19435          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19436          */
19437         mouseover : true,
19438         /**
19439          * @event mouseout
19440          * Fires when the mouse exits this menu
19441          * @param {Roo.menu.Menu} this
19442          * @param {Roo.EventObject} e
19443          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19444          */
19445         mouseout : true,
19446         /**
19447          * @event itemclick
19448          * Fires when a menu item contained in this menu is clicked
19449          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19450          * @param {Roo.EventObject} e
19451          */
19452         itemclick: true
19453     });
19454     if (this.registerMenu) {
19455         Roo.menu.MenuMgr.register(this);
19456     }
19457     
19458     var mis = this.items;
19459     this.items = new Roo.util.MixedCollection();
19460     if(mis){
19461         this.add.apply(this, mis);
19462     }
19463 };
19464
19465 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19466     /**
19467      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19468      */
19469     minWidth : 120,
19470     /**
19471      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19472      * for bottom-right shadow (defaults to "sides")
19473      */
19474     shadow : "sides",
19475     /**
19476      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19477      * this menu (defaults to "tl-tr?")
19478      */
19479     subMenuAlign : "tl-tr?",
19480     /**
19481      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19482      * relative to its element of origin (defaults to "tl-bl?")
19483      */
19484     defaultAlign : "tl-bl?",
19485     /**
19486      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19487      */
19488     allowOtherMenus : false,
19489     /**
19490      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19491      */
19492     registerMenu : true,
19493
19494     hidden:true,
19495
19496     // private
19497     render : function(){
19498         if(this.el){
19499             return;
19500         }
19501         var el = this.el = new Roo.Layer({
19502             cls: "x-menu",
19503             shadow:this.shadow,
19504             constrain: false,
19505             parentEl: this.parentEl || document.body,
19506             zindex:15000
19507         });
19508
19509         this.keyNav = new Roo.menu.MenuNav(this);
19510
19511         if(this.plain){
19512             el.addClass("x-menu-plain");
19513         }
19514         if(this.cls){
19515             el.addClass(this.cls);
19516         }
19517         // generic focus element
19518         this.focusEl = el.createChild({
19519             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19520         });
19521         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19522         ul.on("click", this.onClick, this);
19523         ul.on("mouseover", this.onMouseOver, this);
19524         ul.on("mouseout", this.onMouseOut, this);
19525         this.items.each(function(item){
19526             var li = document.createElement("li");
19527             li.className = "x-menu-list-item";
19528             ul.dom.appendChild(li);
19529             item.render(li, this);
19530         }, this);
19531         this.ul = ul;
19532         this.autoWidth();
19533     },
19534
19535     // private
19536     autoWidth : function(){
19537         var el = this.el, ul = this.ul;
19538         if(!el){
19539             return;
19540         }
19541         var w = this.width;
19542         if(w){
19543             el.setWidth(w);
19544         }else if(Roo.isIE){
19545             el.setWidth(this.minWidth);
19546             var t = el.dom.offsetWidth; // force recalc
19547             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19548         }
19549     },
19550
19551     // private
19552     delayAutoWidth : function(){
19553         if(this.rendered){
19554             if(!this.awTask){
19555                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19556             }
19557             this.awTask.delay(20);
19558         }
19559     },
19560
19561     // private
19562     findTargetItem : function(e){
19563         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19564         if(t && t.menuItemId){
19565             return this.items.get(t.menuItemId);
19566         }
19567     },
19568
19569     // private
19570     onClick : function(e){
19571         var t;
19572         if(t = this.findTargetItem(e)){
19573             t.onClick(e);
19574             this.fireEvent("click", this, t, e);
19575         }
19576     },
19577
19578     // private
19579     setActiveItem : function(item, autoExpand){
19580         if(item != this.activeItem){
19581             if(this.activeItem){
19582                 this.activeItem.deactivate();
19583             }
19584             this.activeItem = item;
19585             item.activate(autoExpand);
19586         }else if(autoExpand){
19587             item.expandMenu();
19588         }
19589     },
19590
19591     // private
19592     tryActivate : function(start, step){
19593         var items = this.items;
19594         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19595             var item = items.get(i);
19596             if(!item.disabled && item.canActivate){
19597                 this.setActiveItem(item, false);
19598                 return item;
19599             }
19600         }
19601         return false;
19602     },
19603
19604     // private
19605     onMouseOver : function(e){
19606         var t;
19607         if(t = this.findTargetItem(e)){
19608             if(t.canActivate && !t.disabled){
19609                 this.setActiveItem(t, true);
19610             }
19611         }
19612         this.fireEvent("mouseover", this, e, t);
19613     },
19614
19615     // private
19616     onMouseOut : function(e){
19617         var t;
19618         if(t = this.findTargetItem(e)){
19619             if(t == this.activeItem && t.shouldDeactivate(e)){
19620                 this.activeItem.deactivate();
19621                 delete this.activeItem;
19622             }
19623         }
19624         this.fireEvent("mouseout", this, e, t);
19625     },
19626
19627     /**
19628      * Read-only.  Returns true if the menu is currently displayed, else false.
19629      * @type Boolean
19630      */
19631     isVisible : function(){
19632         return this.el && !this.hidden;
19633     },
19634
19635     /**
19636      * Displays this menu relative to another element
19637      * @param {String/HTMLElement/Roo.Element} element The element to align to
19638      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19639      * the element (defaults to this.defaultAlign)
19640      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19641      */
19642     show : function(el, pos, parentMenu){
19643         this.parentMenu = parentMenu;
19644         if(!this.el){
19645             this.render();
19646         }
19647         this.fireEvent("beforeshow", this);
19648         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19649     },
19650
19651     /**
19652      * Displays this menu at a specific xy position
19653      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19654      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19655      */
19656     showAt : function(xy, parentMenu, /* private: */_e){
19657         this.parentMenu = parentMenu;
19658         if(!this.el){
19659             this.render();
19660         }
19661         if(_e !== false){
19662             this.fireEvent("beforeshow", this);
19663             xy = this.el.adjustForConstraints(xy);
19664         }
19665         this.el.setXY(xy);
19666         this.el.show();
19667         this.hidden = false;
19668         this.focus();
19669         this.fireEvent("show", this);
19670     },
19671
19672     focus : function(){
19673         if(!this.hidden){
19674             this.doFocus.defer(50, this);
19675         }
19676     },
19677
19678     doFocus : function(){
19679         if(!this.hidden){
19680             this.focusEl.focus();
19681         }
19682     },
19683
19684     /**
19685      * Hides this menu and optionally all parent menus
19686      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19687      */
19688     hide : function(deep){
19689         if(this.el && this.isVisible()){
19690             this.fireEvent("beforehide", this);
19691             if(this.activeItem){
19692                 this.activeItem.deactivate();
19693                 this.activeItem = null;
19694             }
19695             this.el.hide();
19696             this.hidden = true;
19697             this.fireEvent("hide", this);
19698         }
19699         if(deep === true && this.parentMenu){
19700             this.parentMenu.hide(true);
19701         }
19702     },
19703
19704     /**
19705      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19706      * Any of the following are valid:
19707      * <ul>
19708      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19709      * <li>An HTMLElement object which will be converted to a menu item</li>
19710      * <li>A menu item config object that will be created as a new menu item</li>
19711      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19712      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19713      * </ul>
19714      * Usage:
19715      * <pre><code>
19716 // Create the menu
19717 var menu = new Roo.menu.Menu();
19718
19719 // Create a menu item to add by reference
19720 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19721
19722 // Add a bunch of items at once using different methods.
19723 // Only the last item added will be returned.
19724 var item = menu.add(
19725     menuItem,                // add existing item by ref
19726     'Dynamic Item',          // new TextItem
19727     '-',                     // new separator
19728     { text: 'Config Item' }  // new item by config
19729 );
19730 </code></pre>
19731      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19732      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19733      */
19734     add : function(){
19735         var a = arguments, l = a.length, item;
19736         for(var i = 0; i < l; i++){
19737             var el = a[i];
19738             if ((typeof(el) == "object") && el.xtype && el.xns) {
19739                 el = Roo.factory(el, Roo.menu);
19740             }
19741             
19742             if(el.render){ // some kind of Item
19743                 item = this.addItem(el);
19744             }else if(typeof el == "string"){ // string
19745                 if(el == "separator" || el == "-"){
19746                     item = this.addSeparator();
19747                 }else{
19748                     item = this.addText(el);
19749                 }
19750             }else if(el.tagName || el.el){ // element
19751                 item = this.addElement(el);
19752             }else if(typeof el == "object"){ // must be menu item config?
19753                 item = this.addMenuItem(el);
19754             }
19755         }
19756         return item;
19757     },
19758
19759     /**
19760      * Returns this menu's underlying {@link Roo.Element} object
19761      * @return {Roo.Element} The element
19762      */
19763     getEl : function(){
19764         if(!this.el){
19765             this.render();
19766         }
19767         return this.el;
19768     },
19769
19770     /**
19771      * Adds a separator bar to the menu
19772      * @return {Roo.menu.Item} The menu item that was added
19773      */
19774     addSeparator : function(){
19775         return this.addItem(new Roo.menu.Separator());
19776     },
19777
19778     /**
19779      * Adds an {@link Roo.Element} object to the menu
19780      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19781      * @return {Roo.menu.Item} The menu item that was added
19782      */
19783     addElement : function(el){
19784         return this.addItem(new Roo.menu.BaseItem(el));
19785     },
19786
19787     /**
19788      * Adds an existing object based on {@link Roo.menu.Item} to the menu
19789      * @param {Roo.menu.Item} item The menu item to add
19790      * @return {Roo.menu.Item} The menu item that was added
19791      */
19792     addItem : function(item){
19793         this.items.add(item);
19794         if(this.ul){
19795             var li = document.createElement("li");
19796             li.className = "x-menu-list-item";
19797             this.ul.dom.appendChild(li);
19798             item.render(li, this);
19799             this.delayAutoWidth();
19800         }
19801         return item;
19802     },
19803
19804     /**
19805      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
19806      * @param {Object} config A MenuItem config object
19807      * @return {Roo.menu.Item} The menu item that was added
19808      */
19809     addMenuItem : function(config){
19810         if(!(config instanceof Roo.menu.Item)){
19811             if(typeof config.checked == "boolean"){ // must be check menu item config?
19812                 config = new Roo.menu.CheckItem(config);
19813             }else{
19814                 config = new Roo.menu.Item(config);
19815             }
19816         }
19817         return this.addItem(config);
19818     },
19819
19820     /**
19821      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
19822      * @param {String} text The text to display in the menu item
19823      * @return {Roo.menu.Item} The menu item that was added
19824      */
19825     addText : function(text){
19826         return this.addItem(new Roo.menu.TextItem({ text : text }));
19827     },
19828
19829     /**
19830      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
19831      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
19832      * @param {Roo.menu.Item} item The menu item to add
19833      * @return {Roo.menu.Item} The menu item that was added
19834      */
19835     insert : function(index, item){
19836         this.items.insert(index, item);
19837         if(this.ul){
19838             var li = document.createElement("li");
19839             li.className = "x-menu-list-item";
19840             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
19841             item.render(li, this);
19842             this.delayAutoWidth();
19843         }
19844         return item;
19845     },
19846
19847     /**
19848      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
19849      * @param {Roo.menu.Item} item The menu item to remove
19850      */
19851     remove : function(item){
19852         this.items.removeKey(item.id);
19853         item.destroy();
19854     },
19855
19856     /**
19857      * Removes and destroys all items in the menu
19858      */
19859     removeAll : function(){
19860         var f;
19861         while(f = this.items.first()){
19862             this.remove(f);
19863         }
19864     }
19865 });
19866
19867 // MenuNav is a private utility class used internally by the Menu
19868 Roo.menu.MenuNav = function(menu){
19869     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
19870     this.scope = this.menu = menu;
19871 };
19872
19873 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
19874     doRelay : function(e, h){
19875         var k = e.getKey();
19876         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
19877             this.menu.tryActivate(0, 1);
19878             return false;
19879         }
19880         return h.call(this.scope || this, e, this.menu);
19881     },
19882
19883     up : function(e, m){
19884         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
19885             m.tryActivate(m.items.length-1, -1);
19886         }
19887     },
19888
19889     down : function(e, m){
19890         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
19891             m.tryActivate(0, 1);
19892         }
19893     },
19894
19895     right : function(e, m){
19896         if(m.activeItem){
19897             m.activeItem.expandMenu(true);
19898         }
19899     },
19900
19901     left : function(e, m){
19902         m.hide();
19903         if(m.parentMenu && m.parentMenu.activeItem){
19904             m.parentMenu.activeItem.activate();
19905         }
19906     },
19907
19908     enter : function(e, m){
19909         if(m.activeItem){
19910             e.stopPropagation();
19911             m.activeItem.onClick(e);
19912             m.fireEvent("click", this, m.activeItem);
19913             return true;
19914         }
19915     }
19916 });/*
19917  * Based on:
19918  * Ext JS Library 1.1.1
19919  * Copyright(c) 2006-2007, Ext JS, LLC.
19920  *
19921  * Originally Released Under LGPL - original licence link has changed is not relivant.
19922  *
19923  * Fork - LGPL
19924  * <script type="text/javascript">
19925  */
19926  
19927 /**
19928  * @class Roo.menu.MenuMgr
19929  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
19930  * @singleton
19931  */
19932 Roo.menu.MenuMgr = function(){
19933    var menus, active, groups = {}, attached = false, lastShow = new Date();
19934
19935    // private - called when first menu is created
19936    function init(){
19937        menus = {};
19938        active = new Roo.util.MixedCollection();
19939        Roo.get(document).addKeyListener(27, function(){
19940            if(active.length > 0){
19941                hideAll();
19942            }
19943        });
19944    }
19945
19946    // private
19947    function hideAll(){
19948        if(active && active.length > 0){
19949            var c = active.clone();
19950            c.each(function(m){
19951                m.hide();
19952            });
19953        }
19954    }
19955
19956    // private
19957    function onHide(m){
19958        active.remove(m);
19959        if(active.length < 1){
19960            Roo.get(document).un("mousedown", onMouseDown);
19961            attached = false;
19962        }
19963    }
19964
19965    // private
19966    function onShow(m){
19967        var last = active.last();
19968        lastShow = new Date();
19969        active.add(m);
19970        if(!attached){
19971            Roo.get(document).on("mousedown", onMouseDown);
19972            attached = true;
19973        }
19974        if(m.parentMenu){
19975           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
19976           m.parentMenu.activeChild = m;
19977        }else if(last && last.isVisible()){
19978           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
19979        }
19980    }
19981
19982    // private
19983    function onBeforeHide(m){
19984        if(m.activeChild){
19985            m.activeChild.hide();
19986        }
19987        if(m.autoHideTimer){
19988            clearTimeout(m.autoHideTimer);
19989            delete m.autoHideTimer;
19990        }
19991    }
19992
19993    // private
19994    function onBeforeShow(m){
19995        var pm = m.parentMenu;
19996        if(!pm && !m.allowOtherMenus){
19997            hideAll();
19998        }else if(pm && pm.activeChild && active != m){
19999            pm.activeChild.hide();
20000        }
20001    }
20002
20003    // private
20004    function onMouseDown(e){
20005        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
20006            hideAll();
20007        }
20008    }
20009
20010    // private
20011    function onBeforeCheck(mi, state){
20012        if(state){
20013            var g = groups[mi.group];
20014            for(var i = 0, l = g.length; i < l; i++){
20015                if(g[i] != mi){
20016                    g[i].setChecked(false);
20017                }
20018            }
20019        }
20020    }
20021
20022    return {
20023
20024        /**
20025         * Hides all menus that are currently visible
20026         */
20027        hideAll : function(){
20028             hideAll();  
20029        },
20030
20031        // private
20032        register : function(menu){
20033            if(!menus){
20034                init();
20035            }
20036            menus[menu.id] = menu;
20037            menu.on("beforehide", onBeforeHide);
20038            menu.on("hide", onHide);
20039            menu.on("beforeshow", onBeforeShow);
20040            menu.on("show", onShow);
20041            var g = menu.group;
20042            if(g && menu.events["checkchange"]){
20043                if(!groups[g]){
20044                    groups[g] = [];
20045                }
20046                groups[g].push(menu);
20047                menu.on("checkchange", onCheck);
20048            }
20049        },
20050
20051         /**
20052          * Returns a {@link Roo.menu.Menu} object
20053          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
20054          * be used to generate and return a new Menu instance.
20055          */
20056        get : function(menu){
20057            if(typeof menu == "string"){ // menu id
20058                return menus[menu];
20059            }else if(menu.events){  // menu instance
20060                return menu;
20061            }else if(typeof menu.length == 'number'){ // array of menu items?
20062                return new Roo.menu.Menu({items:menu});
20063            }else{ // otherwise, must be a config
20064                return new Roo.menu.Menu(menu);
20065            }
20066        },
20067
20068        // private
20069        unregister : function(menu){
20070            delete menus[menu.id];
20071            menu.un("beforehide", onBeforeHide);
20072            menu.un("hide", onHide);
20073            menu.un("beforeshow", onBeforeShow);
20074            menu.un("show", onShow);
20075            var g = menu.group;
20076            if(g && menu.events["checkchange"]){
20077                groups[g].remove(menu);
20078                menu.un("checkchange", onCheck);
20079            }
20080        },
20081
20082        // private
20083        registerCheckable : function(menuItem){
20084            var g = menuItem.group;
20085            if(g){
20086                if(!groups[g]){
20087                    groups[g] = [];
20088                }
20089                groups[g].push(menuItem);
20090                menuItem.on("beforecheckchange", onBeforeCheck);
20091            }
20092        },
20093
20094        // private
20095        unregisterCheckable : function(menuItem){
20096            var g = menuItem.group;
20097            if(g){
20098                groups[g].remove(menuItem);
20099                menuItem.un("beforecheckchange", onBeforeCheck);
20100            }
20101        }
20102    };
20103 }();/*
20104  * Based on:
20105  * Ext JS Library 1.1.1
20106  * Copyright(c) 2006-2007, Ext JS, LLC.
20107  *
20108  * Originally Released Under LGPL - original licence link has changed is not relivant.
20109  *
20110  * Fork - LGPL
20111  * <script type="text/javascript">
20112  */
20113  
20114
20115 /**
20116  * @class Roo.menu.BaseItem
20117  * @extends Roo.Component
20118  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
20119  * management and base configuration options shared by all menu components.
20120  * @constructor
20121  * Creates a new BaseItem
20122  * @param {Object} config Configuration options
20123  */
20124 Roo.menu.BaseItem = function(config){
20125     Roo.menu.BaseItem.superclass.constructor.call(this, config);
20126
20127     this.addEvents({
20128         /**
20129          * @event click
20130          * Fires when this item is clicked
20131          * @param {Roo.menu.BaseItem} this
20132          * @param {Roo.EventObject} e
20133          */
20134         click: true,
20135         /**
20136          * @event activate
20137          * Fires when this item is activated
20138          * @param {Roo.menu.BaseItem} this
20139          */
20140         activate : true,
20141         /**
20142          * @event deactivate
20143          * Fires when this item is deactivated
20144          * @param {Roo.menu.BaseItem} this
20145          */
20146         deactivate : true
20147     });
20148
20149     if(this.handler){
20150         this.on("click", this.handler, this.scope, true);
20151     }
20152 };
20153
20154 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20155     /**
20156      * @cfg {Function} handler
20157      * A function that will handle the click event of this menu item (defaults to undefined)
20158      */
20159     /**
20160      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20161      */
20162     canActivate : false,
20163     /**
20164      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20165      */
20166     activeClass : "x-menu-item-active",
20167     /**
20168      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20169      */
20170     hideOnClick : true,
20171     /**
20172      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20173      */
20174     hideDelay : 100,
20175
20176     // private
20177     ctype: "Roo.menu.BaseItem",
20178
20179     // private
20180     actionMode : "container",
20181
20182     // private
20183     render : function(container, parentMenu){
20184         this.parentMenu = parentMenu;
20185         Roo.menu.BaseItem.superclass.render.call(this, container);
20186         this.container.menuItemId = this.id;
20187     },
20188
20189     // private
20190     onRender : function(container, position){
20191         this.el = Roo.get(this.el);
20192         container.dom.appendChild(this.el.dom);
20193     },
20194
20195     // private
20196     onClick : function(e){
20197         if(!this.disabled && this.fireEvent("click", this, e) !== false
20198                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20199             this.handleClick(e);
20200         }else{
20201             e.stopEvent();
20202         }
20203     },
20204
20205     // private
20206     activate : function(){
20207         if(this.disabled){
20208             return false;
20209         }
20210         var li = this.container;
20211         li.addClass(this.activeClass);
20212         this.region = li.getRegion().adjust(2, 2, -2, -2);
20213         this.fireEvent("activate", this);
20214         return true;
20215     },
20216
20217     // private
20218     deactivate : function(){
20219         this.container.removeClass(this.activeClass);
20220         this.fireEvent("deactivate", this);
20221     },
20222
20223     // private
20224     shouldDeactivate : function(e){
20225         return !this.region || !this.region.contains(e.getPoint());
20226     },
20227
20228     // private
20229     handleClick : function(e){
20230         if(this.hideOnClick){
20231             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20232         }
20233     },
20234
20235     // private
20236     expandMenu : function(autoActivate){
20237         // do nothing
20238     },
20239
20240     // private
20241     hideMenu : function(){
20242         // do nothing
20243     }
20244 });/*
20245  * Based on:
20246  * Ext JS Library 1.1.1
20247  * Copyright(c) 2006-2007, Ext JS, LLC.
20248  *
20249  * Originally Released Under LGPL - original licence link has changed is not relivant.
20250  *
20251  * Fork - LGPL
20252  * <script type="text/javascript">
20253  */
20254  
20255 /**
20256  * @class Roo.menu.Adapter
20257  * @extends Roo.menu.BaseItem
20258  * 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.
20259  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20260  * @constructor
20261  * Creates a new Adapter
20262  * @param {Object} config Configuration options
20263  */
20264 Roo.menu.Adapter = function(component, config){
20265     Roo.menu.Adapter.superclass.constructor.call(this, config);
20266     this.component = component;
20267 };
20268 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20269     // private
20270     canActivate : true,
20271
20272     // private
20273     onRender : function(container, position){
20274         this.component.render(container);
20275         this.el = this.component.getEl();
20276     },
20277
20278     // private
20279     activate : function(){
20280         if(this.disabled){
20281             return false;
20282         }
20283         this.component.focus();
20284         this.fireEvent("activate", this);
20285         return true;
20286     },
20287
20288     // private
20289     deactivate : function(){
20290         this.fireEvent("deactivate", this);
20291     },
20292
20293     // private
20294     disable : function(){
20295         this.component.disable();
20296         Roo.menu.Adapter.superclass.disable.call(this);
20297     },
20298
20299     // private
20300     enable : function(){
20301         this.component.enable();
20302         Roo.menu.Adapter.superclass.enable.call(this);
20303     }
20304 });/*
20305  * Based on:
20306  * Ext JS Library 1.1.1
20307  * Copyright(c) 2006-2007, Ext JS, LLC.
20308  *
20309  * Originally Released Under LGPL - original licence link has changed is not relivant.
20310  *
20311  * Fork - LGPL
20312  * <script type="text/javascript">
20313  */
20314
20315 /**
20316  * @class Roo.menu.TextItem
20317  * @extends Roo.menu.BaseItem
20318  * Adds a static text string to a menu, usually used as either a heading or group separator.
20319  * Note: old style constructor with text is still supported.
20320  * 
20321  * @constructor
20322  * Creates a new TextItem
20323  * @param {Object} cfg Configuration
20324  */
20325 Roo.menu.TextItem = function(cfg){
20326     if (typeof(cfg) == 'string') {
20327         this.text = cfg;
20328     } else {
20329         Roo.apply(this,cfg);
20330     }
20331     
20332     Roo.menu.TextItem.superclass.constructor.call(this);
20333 };
20334
20335 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20336     /**
20337      * @cfg {Boolean} text Text to show on item.
20338      */
20339     text : '',
20340     
20341     /**
20342      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20343      */
20344     hideOnClick : false,
20345     /**
20346      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20347      */
20348     itemCls : "x-menu-text",
20349
20350     // private
20351     onRender : function(){
20352         var s = document.createElement("span");
20353         s.className = this.itemCls;
20354         s.innerHTML = this.text;
20355         this.el = s;
20356         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20357     }
20358 });/*
20359  * Based on:
20360  * Ext JS Library 1.1.1
20361  * Copyright(c) 2006-2007, Ext JS, LLC.
20362  *
20363  * Originally Released Under LGPL - original licence link has changed is not relivant.
20364  *
20365  * Fork - LGPL
20366  * <script type="text/javascript">
20367  */
20368
20369 /**
20370  * @class Roo.menu.Separator
20371  * @extends Roo.menu.BaseItem
20372  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20373  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20374  * @constructor
20375  * @param {Object} config Configuration options
20376  */
20377 Roo.menu.Separator = function(config){
20378     Roo.menu.Separator.superclass.constructor.call(this, config);
20379 };
20380
20381 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20382     /**
20383      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20384      */
20385     itemCls : "x-menu-sep",
20386     /**
20387      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20388      */
20389     hideOnClick : false,
20390
20391     // private
20392     onRender : function(li){
20393         var s = document.createElement("span");
20394         s.className = this.itemCls;
20395         s.innerHTML = "&#160;";
20396         this.el = s;
20397         li.addClass("x-menu-sep-li");
20398         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20399     }
20400 });/*
20401  * Based on:
20402  * Ext JS Library 1.1.1
20403  * Copyright(c) 2006-2007, Ext JS, LLC.
20404  *
20405  * Originally Released Under LGPL - original licence link has changed is not relivant.
20406  *
20407  * Fork - LGPL
20408  * <script type="text/javascript">
20409  */
20410 /**
20411  * @class Roo.menu.Item
20412  * @extends Roo.menu.BaseItem
20413  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20414  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20415  * activation and click handling.
20416  * @constructor
20417  * Creates a new Item
20418  * @param {Object} config Configuration options
20419  */
20420 Roo.menu.Item = function(config){
20421     Roo.menu.Item.superclass.constructor.call(this, config);
20422     if(this.menu){
20423         this.menu = Roo.menu.MenuMgr.get(this.menu);
20424     }
20425 };
20426 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20427     
20428     /**
20429      * @cfg {String} text
20430      * The text to show on the menu item.
20431      */
20432     text: '',
20433      /**
20434      * @cfg {String} HTML to render in menu
20435      * The text to show on the menu item (HTML version).
20436      */
20437     html: '',
20438     /**
20439      * @cfg {String} icon
20440      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20441      */
20442     icon: undefined,
20443     /**
20444      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20445      */
20446     itemCls : "x-menu-item",
20447     /**
20448      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20449      */
20450     canActivate : true,
20451     /**
20452      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20453      */
20454     showDelay: 200,
20455     // doc'd in BaseItem
20456     hideDelay: 200,
20457
20458     // private
20459     ctype: "Roo.menu.Item",
20460     
20461     // private
20462     onRender : function(container, position){
20463         var el = document.createElement("a");
20464         el.hideFocus = true;
20465         el.unselectable = "on";
20466         el.href = this.href || "#";
20467         if(this.hrefTarget){
20468             el.target = this.hrefTarget;
20469         }
20470         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20471         
20472         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20473         
20474         el.innerHTML = String.format(
20475                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20476                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20477         this.el = el;
20478         Roo.menu.Item.superclass.onRender.call(this, container, position);
20479     },
20480
20481     /**
20482      * Sets the text to display in this menu item
20483      * @param {String} text The text to display
20484      * @param {Boolean} isHTML true to indicate text is pure html.
20485      */
20486     setText : function(text, isHTML){
20487         if (isHTML) {
20488             this.html = text;
20489         } else {
20490             this.text = text;
20491             this.html = '';
20492         }
20493         if(this.rendered){
20494             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20495      
20496             this.el.update(String.format(
20497                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20498                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20499             this.parentMenu.autoWidth();
20500         }
20501     },
20502
20503     // private
20504     handleClick : function(e){
20505         if(!this.href){ // if no link defined, stop the event automatically
20506             e.stopEvent();
20507         }
20508         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20509     },
20510
20511     // private
20512     activate : function(autoExpand){
20513         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20514             this.focus();
20515             if(autoExpand){
20516                 this.expandMenu();
20517             }
20518         }
20519         return true;
20520     },
20521
20522     // private
20523     shouldDeactivate : function(e){
20524         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20525             if(this.menu && this.menu.isVisible()){
20526                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20527             }
20528             return true;
20529         }
20530         return false;
20531     },
20532
20533     // private
20534     deactivate : function(){
20535         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20536         this.hideMenu();
20537     },
20538
20539     // private
20540     expandMenu : function(autoActivate){
20541         if(!this.disabled && this.menu){
20542             clearTimeout(this.hideTimer);
20543             delete this.hideTimer;
20544             if(!this.menu.isVisible() && !this.showTimer){
20545                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20546             }else if (this.menu.isVisible() && autoActivate){
20547                 this.menu.tryActivate(0, 1);
20548             }
20549         }
20550     },
20551
20552     // private
20553     deferExpand : function(autoActivate){
20554         delete this.showTimer;
20555         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20556         if(autoActivate){
20557             this.menu.tryActivate(0, 1);
20558         }
20559     },
20560
20561     // private
20562     hideMenu : function(){
20563         clearTimeout(this.showTimer);
20564         delete this.showTimer;
20565         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20566             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20567         }
20568     },
20569
20570     // private
20571     deferHide : function(){
20572         delete this.hideTimer;
20573         this.menu.hide();
20574     }
20575 });/*
20576  * Based on:
20577  * Ext JS Library 1.1.1
20578  * Copyright(c) 2006-2007, Ext JS, LLC.
20579  *
20580  * Originally Released Under LGPL - original licence link has changed is not relivant.
20581  *
20582  * Fork - LGPL
20583  * <script type="text/javascript">
20584  */
20585  
20586 /**
20587  * @class Roo.menu.CheckItem
20588  * @extends Roo.menu.Item
20589  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20590  * @constructor
20591  * Creates a new CheckItem
20592  * @param {Object} config Configuration options
20593  */
20594 Roo.menu.CheckItem = function(config){
20595     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20596     this.addEvents({
20597         /**
20598          * @event beforecheckchange
20599          * Fires before the checked value is set, providing an opportunity to cancel if needed
20600          * @param {Roo.menu.CheckItem} this
20601          * @param {Boolean} checked The new checked value that will be set
20602          */
20603         "beforecheckchange" : true,
20604         /**
20605          * @event checkchange
20606          * Fires after the checked value has been set
20607          * @param {Roo.menu.CheckItem} this
20608          * @param {Boolean} checked The checked value that was set
20609          */
20610         "checkchange" : true
20611     });
20612     if(this.checkHandler){
20613         this.on('checkchange', this.checkHandler, this.scope);
20614     }
20615 };
20616 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20617     /**
20618      * @cfg {String} group
20619      * All check items with the same group name will automatically be grouped into a single-select
20620      * radio button group (defaults to '')
20621      */
20622     /**
20623      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20624      */
20625     itemCls : "x-menu-item x-menu-check-item",
20626     /**
20627      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20628      */
20629     groupClass : "x-menu-group-item",
20630
20631     /**
20632      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20633      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20634      * initialized with checked = true will be rendered as checked.
20635      */
20636     checked: false,
20637
20638     // private
20639     ctype: "Roo.menu.CheckItem",
20640
20641     // private
20642     onRender : function(c){
20643         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20644         if(this.group){
20645             this.el.addClass(this.groupClass);
20646         }
20647         Roo.menu.MenuMgr.registerCheckable(this);
20648         if(this.checked){
20649             this.checked = false;
20650             this.setChecked(true, true);
20651         }
20652     },
20653
20654     // private
20655     destroy : function(){
20656         if(this.rendered){
20657             Roo.menu.MenuMgr.unregisterCheckable(this);
20658         }
20659         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20660     },
20661
20662     /**
20663      * Set the checked state of this item
20664      * @param {Boolean} checked The new checked value
20665      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20666      */
20667     setChecked : function(state, suppressEvent){
20668         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20669             if(this.container){
20670                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20671             }
20672             this.checked = state;
20673             if(suppressEvent !== true){
20674                 this.fireEvent("checkchange", this, state);
20675             }
20676         }
20677     },
20678
20679     // private
20680     handleClick : function(e){
20681        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20682            this.setChecked(!this.checked);
20683        }
20684        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20685     }
20686 });/*
20687  * Based on:
20688  * Ext JS Library 1.1.1
20689  * Copyright(c) 2006-2007, Ext JS, LLC.
20690  *
20691  * Originally Released Under LGPL - original licence link has changed is not relivant.
20692  *
20693  * Fork - LGPL
20694  * <script type="text/javascript">
20695  */
20696  
20697 /**
20698  * @class Roo.menu.DateItem
20699  * @extends Roo.menu.Adapter
20700  * A menu item that wraps the {@link Roo.DatPicker} component.
20701  * @constructor
20702  * Creates a new DateItem
20703  * @param {Object} config Configuration options
20704  */
20705 Roo.menu.DateItem = function(config){
20706     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20707     /** The Roo.DatePicker object @type Roo.DatePicker */
20708     this.picker = this.component;
20709     this.addEvents({select: true});
20710     
20711     this.picker.on("render", function(picker){
20712         picker.getEl().swallowEvent("click");
20713         picker.container.addClass("x-menu-date-item");
20714     });
20715
20716     this.picker.on("select", this.onSelect, this);
20717 };
20718
20719 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20720     // private
20721     onSelect : function(picker, date){
20722         this.fireEvent("select", this, date, picker);
20723         Roo.menu.DateItem.superclass.handleClick.call(this);
20724     }
20725 });/*
20726  * Based on:
20727  * Ext JS Library 1.1.1
20728  * Copyright(c) 2006-2007, Ext JS, LLC.
20729  *
20730  * Originally Released Under LGPL - original licence link has changed is not relivant.
20731  *
20732  * Fork - LGPL
20733  * <script type="text/javascript">
20734  */
20735  
20736 /**
20737  * @class Roo.menu.ColorItem
20738  * @extends Roo.menu.Adapter
20739  * A menu item that wraps the {@link Roo.ColorPalette} component.
20740  * @constructor
20741  * Creates a new ColorItem
20742  * @param {Object} config Configuration options
20743  */
20744 Roo.menu.ColorItem = function(config){
20745     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20746     /** The Roo.ColorPalette object @type Roo.ColorPalette */
20747     this.palette = this.component;
20748     this.relayEvents(this.palette, ["select"]);
20749     if(this.selectHandler){
20750         this.on('select', this.selectHandler, this.scope);
20751     }
20752 };
20753 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
20754  * Based on:
20755  * Ext JS Library 1.1.1
20756  * Copyright(c) 2006-2007, Ext JS, LLC.
20757  *
20758  * Originally Released Under LGPL - original licence link has changed is not relivant.
20759  *
20760  * Fork - LGPL
20761  * <script type="text/javascript">
20762  */
20763  
20764
20765 /**
20766  * @class Roo.menu.DateMenu
20767  * @extends Roo.menu.Menu
20768  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20769  * @constructor
20770  * Creates a new DateMenu
20771  * @param {Object} config Configuration options
20772  */
20773 Roo.menu.DateMenu = function(config){
20774     Roo.menu.DateMenu.superclass.constructor.call(this, config);
20775     this.plain = true;
20776     var di = new Roo.menu.DateItem(config);
20777     this.add(di);
20778     /**
20779      * The {@link Roo.DatePicker} instance for this DateMenu
20780      * @type DatePicker
20781      */
20782     this.picker = di.picker;
20783     /**
20784      * @event select
20785      * @param {DatePicker} picker
20786      * @param {Date} date
20787      */
20788     this.relayEvents(di, ["select"]);
20789
20790     this.on('beforeshow', function(){
20791         if(this.picker){
20792             this.picker.hideMonthPicker(true);
20793         }
20794     }, this);
20795 };
20796 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
20797     cls:'x-date-menu'
20798 });/*
20799  * Based on:
20800  * Ext JS Library 1.1.1
20801  * Copyright(c) 2006-2007, Ext JS, LLC.
20802  *
20803  * Originally Released Under LGPL - original licence link has changed is not relivant.
20804  *
20805  * Fork - LGPL
20806  * <script type="text/javascript">
20807  */
20808  
20809
20810 /**
20811  * @class Roo.menu.ColorMenu
20812  * @extends Roo.menu.Menu
20813  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
20814  * @constructor
20815  * Creates a new ColorMenu
20816  * @param {Object} config Configuration options
20817  */
20818 Roo.menu.ColorMenu = function(config){
20819     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
20820     this.plain = true;
20821     var ci = new Roo.menu.ColorItem(config);
20822     this.add(ci);
20823     /**
20824      * The {@link Roo.ColorPalette} instance for this ColorMenu
20825      * @type ColorPalette
20826      */
20827     this.palette = ci.palette;
20828     /**
20829      * @event select
20830      * @param {ColorPalette} palette
20831      * @param {String} color
20832      */
20833     this.relayEvents(ci, ["select"]);
20834 };
20835 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
20836  * Based on:
20837  * Ext JS Library 1.1.1
20838  * Copyright(c) 2006-2007, Ext JS, LLC.
20839  *
20840  * Originally Released Under LGPL - original licence link has changed is not relivant.
20841  *
20842  * Fork - LGPL
20843  * <script type="text/javascript">
20844  */
20845  
20846 /**
20847  * @class Roo.form.Field
20848  * @extends Roo.BoxComponent
20849  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
20850  * @constructor
20851  * Creates a new Field
20852  * @param {Object} config Configuration options
20853  */
20854 Roo.form.Field = function(config){
20855     Roo.form.Field.superclass.constructor.call(this, config);
20856 };
20857
20858 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
20859     /**
20860      * @cfg {String} fieldLabel Label to use when rendering a form.
20861      */
20862        /**
20863      * @cfg {String} qtip Mouse over tip
20864      */
20865      
20866     /**
20867      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
20868      */
20869     invalidClass : "x-form-invalid",
20870     /**
20871      * @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")
20872      */
20873     invalidText : "The value in this field is invalid",
20874     /**
20875      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
20876      */
20877     focusClass : "x-form-focus",
20878     /**
20879      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
20880       automatic validation (defaults to "keyup").
20881      */
20882     validationEvent : "keyup",
20883     /**
20884      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
20885      */
20886     validateOnBlur : true,
20887     /**
20888      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
20889      */
20890     validationDelay : 250,
20891     /**
20892      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20893      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
20894      */
20895     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
20896     /**
20897      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
20898      */
20899     fieldClass : "x-form-field",
20900     /**
20901      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
20902      *<pre>
20903 Value         Description
20904 -----------   ----------------------------------------------------------------------
20905 qtip          Display a quick tip when the user hovers over the field
20906 title         Display a default browser title attribute popup
20907 under         Add a block div beneath the field containing the error text
20908 side          Add an error icon to the right of the field with a popup on hover
20909 [element id]  Add the error text directly to the innerHTML of the specified element
20910 </pre>
20911      */
20912     msgTarget : 'qtip',
20913     /**
20914      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
20915      */
20916     msgFx : 'normal',
20917
20918     /**
20919      * @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.
20920      */
20921     readOnly : false,
20922
20923     /**
20924      * @cfg {Boolean} disabled True to disable the field (defaults to false).
20925      */
20926     disabled : false,
20927
20928     /**
20929      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
20930      */
20931     inputType : undefined,
20932     
20933     /**
20934      * @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).
20935          */
20936         tabIndex : undefined,
20937         
20938     // private
20939     isFormField : true,
20940
20941     // private
20942     hasFocus : false,
20943     /**
20944      * @property {Roo.Element} fieldEl
20945      * Element Containing the rendered Field (with label etc.)
20946      */
20947     /**
20948      * @cfg {Mixed} value A value to initialize this field with.
20949      */
20950     value : undefined,
20951
20952     /**
20953      * @cfg {String} name The field's HTML name attribute.
20954      */
20955     /**
20956      * @cfg {String} cls A CSS class to apply to the field's underlying element.
20957      */
20958
20959         // private ??
20960         initComponent : function(){
20961         Roo.form.Field.superclass.initComponent.call(this);
20962         this.addEvents({
20963             /**
20964              * @event focus
20965              * Fires when this field receives input focus.
20966              * @param {Roo.form.Field} this
20967              */
20968             focus : true,
20969             /**
20970              * @event blur
20971              * Fires when this field loses input focus.
20972              * @param {Roo.form.Field} this
20973              */
20974             blur : true,
20975             /**
20976              * @event specialkey
20977              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
20978              * {@link Roo.EventObject#getKey} to determine which key was pressed.
20979              * @param {Roo.form.Field} this
20980              * @param {Roo.EventObject} e The event object
20981              */
20982             specialkey : true,
20983             /**
20984              * @event change
20985              * Fires just before the field blurs if the field value has changed.
20986              * @param {Roo.form.Field} this
20987              * @param {Mixed} newValue The new value
20988              * @param {Mixed} oldValue The original value
20989              */
20990             change : true,
20991             /**
20992              * @event invalid
20993              * Fires after the field has been marked as invalid.
20994              * @param {Roo.form.Field} this
20995              * @param {String} msg The validation message
20996              */
20997             invalid : true,
20998             /**
20999              * @event valid
21000              * Fires after the field has been validated with no errors.
21001              * @param {Roo.form.Field} this
21002              */
21003             valid : true,
21004              /**
21005              * @event keyup
21006              * Fires after the key up
21007              * @param {Roo.form.Field} this
21008              * @param {Roo.EventObject}  e The event Object
21009              */
21010             keyup : true
21011         });
21012     },
21013
21014     /**
21015      * Returns the name attribute of the field if available
21016      * @return {String} name The field name
21017      */
21018     getName: function(){
21019          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
21020     },
21021
21022     // private
21023     onRender : function(ct, position){
21024         Roo.form.Field.superclass.onRender.call(this, ct, position);
21025         if(!this.el){
21026             var cfg = this.getAutoCreate();
21027             if(!cfg.name){
21028                 cfg.name = this.name || this.id;
21029             }
21030             if(this.inputType){
21031                 cfg.type = this.inputType;
21032             }
21033             this.el = ct.createChild(cfg, position);
21034         }
21035         var type = this.el.dom.type;
21036         if(type){
21037             if(type == 'password'){
21038                 type = 'text';
21039             }
21040             this.el.addClass('x-form-'+type);
21041         }
21042         if(this.readOnly){
21043             this.el.dom.readOnly = true;
21044         }
21045         if(this.tabIndex !== undefined){
21046             this.el.dom.setAttribute('tabIndex', this.tabIndex);
21047         }
21048
21049         this.el.addClass([this.fieldClass, this.cls]);
21050         this.initValue();
21051     },
21052
21053     /**
21054      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
21055      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
21056      * @return {Roo.form.Field} this
21057      */
21058     applyTo : function(target){
21059         this.allowDomMove = false;
21060         this.el = Roo.get(target);
21061         this.render(this.el.dom.parentNode);
21062         return this;
21063     },
21064
21065     // private
21066     initValue : function(){
21067         if(this.value !== undefined){
21068             this.setValue(this.value);
21069         }else if(this.el.dom.value.length > 0){
21070             this.setValue(this.el.dom.value);
21071         }
21072     },
21073
21074     /**
21075      * Returns true if this field has been changed since it was originally loaded and is not disabled.
21076      */
21077     isDirty : function() {
21078         if(this.disabled) {
21079             return false;
21080         }
21081         return String(this.getValue()) !== String(this.originalValue);
21082     },
21083
21084     // private
21085     afterRender : function(){
21086         Roo.form.Field.superclass.afterRender.call(this);
21087         this.initEvents();
21088     },
21089
21090     // private
21091     fireKey : function(e){
21092         //Roo.log('field ' + e.getKey());
21093         if(e.isNavKeyPress()){
21094             this.fireEvent("specialkey", this, e);
21095         }
21096     },
21097
21098     /**
21099      * Resets the current field value to the originally loaded value and clears any validation messages
21100      */
21101     reset : function(){
21102         this.setValue(this.originalValue);
21103         this.clearInvalid();
21104     },
21105
21106     // private
21107     initEvents : function(){
21108         // safari killled keypress - so keydown is now used..
21109         this.el.on("keydown" , this.fireKey,  this);
21110         this.el.on("focus", this.onFocus,  this);
21111         this.el.on("blur", this.onBlur,  this);
21112         this.el.relayEvent('keyup', this);
21113
21114         // reference to original value for reset
21115         this.originalValue = this.getValue();
21116     },
21117
21118     // private
21119     onFocus : function(){
21120         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21121             this.el.addClass(this.focusClass);
21122         }
21123         if(!this.hasFocus){
21124             this.hasFocus = true;
21125             this.startValue = this.getValue();
21126             this.fireEvent("focus", this);
21127         }
21128     },
21129
21130     beforeBlur : Roo.emptyFn,
21131
21132     // private
21133     onBlur : function(){
21134         this.beforeBlur();
21135         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21136             this.el.removeClass(this.focusClass);
21137         }
21138         this.hasFocus = false;
21139         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21140             this.validate();
21141         }
21142         var v = this.getValue();
21143         if(String(v) !== String(this.startValue)){
21144             this.fireEvent('change', this, v, this.startValue);
21145         }
21146         this.fireEvent("blur", this);
21147     },
21148
21149     /**
21150      * Returns whether or not the field value is currently valid
21151      * @param {Boolean} preventMark True to disable marking the field invalid
21152      * @return {Boolean} True if the value is valid, else false
21153      */
21154     isValid : function(preventMark){
21155         if(this.disabled){
21156             return true;
21157         }
21158         var restore = this.preventMark;
21159         this.preventMark = preventMark === true;
21160         var v = this.validateValue(this.processValue(this.getRawValue()));
21161         this.preventMark = restore;
21162         return v;
21163     },
21164
21165     /**
21166      * Validates the field value
21167      * @return {Boolean} True if the value is valid, else false
21168      */
21169     validate : function(){
21170         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21171             this.clearInvalid();
21172             return true;
21173         }
21174         return false;
21175     },
21176
21177     processValue : function(value){
21178         return value;
21179     },
21180
21181     // private
21182     // Subclasses should provide the validation implementation by overriding this
21183     validateValue : function(value){
21184         return true;
21185     },
21186
21187     /**
21188      * Mark this field as invalid
21189      * @param {String} msg The validation message
21190      */
21191     markInvalid : function(msg){
21192         if(!this.rendered || this.preventMark){ // not rendered
21193             return;
21194         }
21195         this.el.addClass(this.invalidClass);
21196         msg = msg || this.invalidText;
21197         switch(this.msgTarget){
21198             case 'qtip':
21199                 this.el.dom.qtip = msg;
21200                 this.el.dom.qclass = 'x-form-invalid-tip';
21201                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21202                     Roo.QuickTips.enable();
21203                 }
21204                 break;
21205             case 'title':
21206                 this.el.dom.title = msg;
21207                 break;
21208             case 'under':
21209                 if(!this.errorEl){
21210                     var elp = this.el.findParent('.x-form-element', 5, true);
21211                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21212                     this.errorEl.setWidth(elp.getWidth(true)-20);
21213                 }
21214                 this.errorEl.update(msg);
21215                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21216                 break;
21217             case 'side':
21218                 if(!this.errorIcon){
21219                     var elp = this.el.findParent('.x-form-element', 5, true);
21220                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21221                 }
21222                 this.alignErrorIcon();
21223                 this.errorIcon.dom.qtip = msg;
21224                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21225                 this.errorIcon.show();
21226                 this.on('resize', this.alignErrorIcon, this);
21227                 break;
21228             default:
21229                 var t = Roo.getDom(this.msgTarget);
21230                 t.innerHTML = msg;
21231                 t.style.display = this.msgDisplay;
21232                 break;
21233         }
21234         this.fireEvent('invalid', this, msg);
21235     },
21236
21237     // private
21238     alignErrorIcon : function(){
21239         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21240     },
21241
21242     /**
21243      * Clear any invalid styles/messages for this field
21244      */
21245     clearInvalid : function(){
21246         if(!this.rendered || this.preventMark){ // not rendered
21247             return;
21248         }
21249         this.el.removeClass(this.invalidClass);
21250         switch(this.msgTarget){
21251             case 'qtip':
21252                 this.el.dom.qtip = '';
21253                 break;
21254             case 'title':
21255                 this.el.dom.title = '';
21256                 break;
21257             case 'under':
21258                 if(this.errorEl){
21259                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21260                 }
21261                 break;
21262             case 'side':
21263                 if(this.errorIcon){
21264                     this.errorIcon.dom.qtip = '';
21265                     this.errorIcon.hide();
21266                     this.un('resize', this.alignErrorIcon, this);
21267                 }
21268                 break;
21269             default:
21270                 var t = Roo.getDom(this.msgTarget);
21271                 t.innerHTML = '';
21272                 t.style.display = 'none';
21273                 break;
21274         }
21275         this.fireEvent('valid', this);
21276     },
21277
21278     /**
21279      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21280      * @return {Mixed} value The field value
21281      */
21282     getRawValue : function(){
21283         var v = this.el.getValue();
21284         if(v === this.emptyText){
21285             v = '';
21286         }
21287         return v;
21288     },
21289
21290     /**
21291      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21292      * @return {Mixed} value The field value
21293      */
21294     getValue : function(){
21295         var v = this.el.getValue();
21296         if(v === this.emptyText || v === undefined){
21297             v = '';
21298         }
21299         return v;
21300     },
21301
21302     /**
21303      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21304      * @param {Mixed} value The value to set
21305      */
21306     setRawValue : function(v){
21307         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21308     },
21309
21310     /**
21311      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21312      * @param {Mixed} value The value to set
21313      */
21314     setValue : function(v){
21315         this.value = v;
21316         if(this.rendered){
21317             this.el.dom.value = (v === null || v === undefined ? '' : v);
21318             this.validate();
21319         }
21320     },
21321
21322     adjustSize : function(w, h){
21323         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21324         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21325         return s;
21326     },
21327
21328     adjustWidth : function(tag, w){
21329         tag = tag.toLowerCase();
21330         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21331             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21332                 if(tag == 'input'){
21333                     return w + 2;
21334                 }
21335                 if(tag = 'textarea'){
21336                     return w-2;
21337                 }
21338             }else if(Roo.isOpera){
21339                 if(tag == 'input'){
21340                     return w + 2;
21341                 }
21342                 if(tag = 'textarea'){
21343                     return w-2;
21344                 }
21345             }
21346         }
21347         return w;
21348     }
21349 });
21350
21351
21352 // anything other than normal should be considered experimental
21353 Roo.form.Field.msgFx = {
21354     normal : {
21355         show: function(msgEl, f){
21356             msgEl.setDisplayed('block');
21357         },
21358
21359         hide : function(msgEl, f){
21360             msgEl.setDisplayed(false).update('');
21361         }
21362     },
21363
21364     slide : {
21365         show: function(msgEl, f){
21366             msgEl.slideIn('t', {stopFx:true});
21367         },
21368
21369         hide : function(msgEl, f){
21370             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21371         }
21372     },
21373
21374     slideRight : {
21375         show: function(msgEl, f){
21376             msgEl.fixDisplay();
21377             msgEl.alignTo(f.el, 'tl-tr');
21378             msgEl.slideIn('l', {stopFx:true});
21379         },
21380
21381         hide : function(msgEl, f){
21382             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21383         }
21384     }
21385 };/*
21386  * Based on:
21387  * Ext JS Library 1.1.1
21388  * Copyright(c) 2006-2007, Ext JS, LLC.
21389  *
21390  * Originally Released Under LGPL - original licence link has changed is not relivant.
21391  *
21392  * Fork - LGPL
21393  * <script type="text/javascript">
21394  */
21395  
21396
21397 /**
21398  * @class Roo.form.TextField
21399  * @extends Roo.form.Field
21400  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21401  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21402  * @constructor
21403  * Creates a new TextField
21404  * @param {Object} config Configuration options
21405  */
21406 Roo.form.TextField = function(config){
21407     Roo.form.TextField.superclass.constructor.call(this, config);
21408     this.addEvents({
21409         /**
21410          * @event autosize
21411          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21412          * according to the default logic, but this event provides a hook for the developer to apply additional
21413          * logic at runtime to resize the field if needed.
21414              * @param {Roo.form.Field} this This text field
21415              * @param {Number} width The new field width
21416              */
21417         autosize : true
21418     });
21419 };
21420
21421 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21422     /**
21423      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21424      */
21425     grow : false,
21426     /**
21427      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21428      */
21429     growMin : 30,
21430     /**
21431      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21432      */
21433     growMax : 800,
21434     /**
21435      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21436      */
21437     vtype : null,
21438     /**
21439      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21440      */
21441     maskRe : null,
21442     /**
21443      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21444      */
21445     disableKeyFilter : false,
21446     /**
21447      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21448      */
21449     allowBlank : true,
21450     /**
21451      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21452      */
21453     minLength : 0,
21454     /**
21455      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21456      */
21457     maxLength : Number.MAX_VALUE,
21458     /**
21459      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21460      */
21461     minLengthText : "The minimum length for this field is {0}",
21462     /**
21463      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21464      */
21465     maxLengthText : "The maximum length for this field is {0}",
21466     /**
21467      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21468      */
21469     selectOnFocus : false,
21470     /**
21471      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21472      */
21473     blankText : "This field is required",
21474     /**
21475      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21476      * If available, this function will be called only after the basic validators all return true, and will be passed the
21477      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21478      */
21479     validator : null,
21480     /**
21481      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21482      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21483      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21484      */
21485     regex : null,
21486     /**
21487      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21488      */
21489     regexText : "",
21490     /**
21491      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
21492      */
21493     emptyText : null,
21494     /**
21495      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
21496      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
21497      */
21498     emptyClass : 'x-form-empty-field',
21499
21500     // private
21501     initEvents : function(){
21502         Roo.form.TextField.superclass.initEvents.call(this);
21503         if(this.validationEvent == 'keyup'){
21504             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21505             this.el.on('keyup', this.filterValidation, this);
21506         }
21507         else if(this.validationEvent !== false){
21508             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21509         }
21510         if(this.selectOnFocus || this.emptyText){
21511             this.on("focus", this.preFocus, this);
21512             if(this.emptyText){
21513                 this.on('blur', this.postBlur, this);
21514                 this.applyEmptyText();
21515             }
21516         }
21517         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21518             this.el.on("keypress", this.filterKeys, this);
21519         }
21520         if(this.grow){
21521             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21522             this.el.on("click", this.autoSize,  this);
21523         }
21524     },
21525
21526     processValue : function(value){
21527         if(this.stripCharsRe){
21528             var newValue = value.replace(this.stripCharsRe, '');
21529             if(newValue !== value){
21530                 this.setRawValue(newValue);
21531                 return newValue;
21532             }
21533         }
21534         return value;
21535     },
21536
21537     filterValidation : function(e){
21538         if(!e.isNavKeyPress()){
21539             this.validationTask.delay(this.validationDelay);
21540         }
21541     },
21542
21543     // private
21544     onKeyUp : function(e){
21545         if(!e.isNavKeyPress()){
21546             this.autoSize();
21547         }
21548     },
21549
21550     /**
21551      * Resets the current field value to the originally-loaded value and clears any validation messages.
21552      * Also adds emptyText and emptyClass if the original value was blank.
21553      */
21554     reset : function(){
21555         Roo.form.TextField.superclass.reset.call(this);
21556         this.applyEmptyText();
21557     },
21558
21559     applyEmptyText : function(){
21560         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
21561             this.setRawValue(this.emptyText);
21562             this.el.addClass(this.emptyClass);
21563         }
21564     },
21565
21566     // private
21567     preFocus : function(){
21568         if(this.emptyText){
21569             if(this.el.dom.value == this.emptyText){
21570                 this.setRawValue('');
21571             }
21572             this.el.removeClass(this.emptyClass);
21573         }
21574         if(this.selectOnFocus){
21575             this.el.dom.select();
21576         }
21577     },
21578
21579     // private
21580     postBlur : function(){
21581         this.applyEmptyText();
21582     },
21583
21584     // private
21585     filterKeys : function(e){
21586         var k = e.getKey();
21587         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21588             return;
21589         }
21590         var c = e.getCharCode(), cc = String.fromCharCode(c);
21591         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21592             return;
21593         }
21594         if(!this.maskRe.test(cc)){
21595             e.stopEvent();
21596         }
21597     },
21598
21599     setValue : function(v){
21600         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
21601             this.el.removeClass(this.emptyClass);
21602         }
21603         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21604         this.applyEmptyText();
21605         this.autoSize();
21606     },
21607
21608     /**
21609      * Validates a value according to the field's validation rules and marks the field as invalid
21610      * if the validation fails
21611      * @param {Mixed} value The value to validate
21612      * @return {Boolean} True if the value is valid, else false
21613      */
21614     validateValue : function(value){
21615         if(value.length < 1 || value === this.emptyText){ // if it's blank
21616              if(this.allowBlank){
21617                 this.clearInvalid();
21618                 return true;
21619              }else{
21620                 this.markInvalid(this.blankText);
21621                 return false;
21622              }
21623         }
21624         if(value.length < this.minLength){
21625             this.markInvalid(String.format(this.minLengthText, this.minLength));
21626             return false;
21627         }
21628         if(value.length > this.maxLength){
21629             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21630             return false;
21631         }
21632         if(this.vtype){
21633             var vt = Roo.form.VTypes;
21634             if(!vt[this.vtype](value, this)){
21635                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21636                 return false;
21637             }
21638         }
21639         if(typeof this.validator == "function"){
21640             var msg = this.validator(value);
21641             if(msg !== true){
21642                 this.markInvalid(msg);
21643                 return false;
21644             }
21645         }
21646         if(this.regex && !this.regex.test(value)){
21647             this.markInvalid(this.regexText);
21648             return false;
21649         }
21650         return true;
21651     },
21652
21653     /**
21654      * Selects text in this field
21655      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21656      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21657      */
21658     selectText : function(start, end){
21659         var v = this.getRawValue();
21660         if(v.length > 0){
21661             start = start === undefined ? 0 : start;
21662             end = end === undefined ? v.length : end;
21663             var d = this.el.dom;
21664             if(d.setSelectionRange){
21665                 d.setSelectionRange(start, end);
21666             }else if(d.createTextRange){
21667                 var range = d.createTextRange();
21668                 range.moveStart("character", start);
21669                 range.moveEnd("character", v.length-end);
21670                 range.select();
21671             }
21672         }
21673     },
21674
21675     /**
21676      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21677      * This only takes effect if grow = true, and fires the autosize event.
21678      */
21679     autoSize : function(){
21680         if(!this.grow || !this.rendered){
21681             return;
21682         }
21683         if(!this.metrics){
21684             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21685         }
21686         var el = this.el;
21687         var v = el.dom.value;
21688         var d = document.createElement('div');
21689         d.appendChild(document.createTextNode(v));
21690         v = d.innerHTML;
21691         d = null;
21692         v += "&#160;";
21693         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21694         this.el.setWidth(w);
21695         this.fireEvent("autosize", this, w);
21696     }
21697 });/*
21698  * Based on:
21699  * Ext JS Library 1.1.1
21700  * Copyright(c) 2006-2007, Ext JS, LLC.
21701  *
21702  * Originally Released Under LGPL - original licence link has changed is not relivant.
21703  *
21704  * Fork - LGPL
21705  * <script type="text/javascript">
21706  */
21707  
21708 /**
21709  * @class Roo.form.Hidden
21710  * @extends Roo.form.TextField
21711  * Simple Hidden element used on forms 
21712  * 
21713  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21714  * 
21715  * @constructor
21716  * Creates a new Hidden form element.
21717  * @param {Object} config Configuration options
21718  */
21719
21720
21721
21722 // easy hidden field...
21723 Roo.form.Hidden = function(config){
21724     Roo.form.Hidden.superclass.constructor.call(this, config);
21725 };
21726   
21727 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21728     fieldLabel:      '',
21729     inputType:      'hidden',
21730     width:          50,
21731     allowBlank:     true,
21732     labelSeparator: '',
21733     hidden:         true,
21734     itemCls :       'x-form-item-display-none'
21735
21736
21737 });
21738
21739
21740 /*
21741  * Based on:
21742  * Ext JS Library 1.1.1
21743  * Copyright(c) 2006-2007, Ext JS, LLC.
21744  *
21745  * Originally Released Under LGPL - original licence link has changed is not relivant.
21746  *
21747  * Fork - LGPL
21748  * <script type="text/javascript">
21749  */
21750  
21751 /**
21752  * @class Roo.form.TriggerField
21753  * @extends Roo.form.TextField
21754  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
21755  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
21756  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
21757  * for which you can provide a custom implementation.  For example:
21758  * <pre><code>
21759 var trigger = new Roo.form.TriggerField();
21760 trigger.onTriggerClick = myTriggerFn;
21761 trigger.applyTo('my-field');
21762 </code></pre>
21763  *
21764  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
21765  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
21766  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
21767  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
21768  * @constructor
21769  * Create a new TriggerField.
21770  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
21771  * to the base TextField)
21772  */
21773 Roo.form.TriggerField = function(config){
21774     this.mimicing = false;
21775     Roo.form.TriggerField.superclass.constructor.call(this, config);
21776 };
21777
21778 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
21779     /**
21780      * @cfg {String} triggerClass A CSS class to apply to the trigger
21781      */
21782     /**
21783      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21784      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
21785      */
21786     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
21787     /**
21788      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
21789      */
21790     hideTrigger:false,
21791
21792     /** @cfg {Boolean} grow @hide */
21793     /** @cfg {Number} growMin @hide */
21794     /** @cfg {Number} growMax @hide */
21795
21796     /**
21797      * @hide 
21798      * @method
21799      */
21800     autoSize: Roo.emptyFn,
21801     // private
21802     monitorTab : true,
21803     // private
21804     deferHeight : true,
21805
21806     
21807     actionMode : 'wrap',
21808     // private
21809     onResize : function(w, h){
21810         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
21811         if(typeof w == 'number'){
21812             var x = w - this.trigger.getWidth();
21813             this.el.setWidth(this.adjustWidth('input', x));
21814             this.trigger.setStyle('left', x+'px');
21815         }
21816     },
21817
21818     // private
21819     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21820
21821     // private
21822     getResizeEl : function(){
21823         return this.wrap;
21824     },
21825
21826     // private
21827     getPositionEl : function(){
21828         return this.wrap;
21829     },
21830
21831     // private
21832     alignErrorIcon : function(){
21833         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
21834     },
21835
21836     // private
21837     onRender : function(ct, position){
21838         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
21839         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
21840         this.trigger = this.wrap.createChild(this.triggerConfig ||
21841                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
21842         if(this.hideTrigger){
21843             this.trigger.setDisplayed(false);
21844         }
21845         this.initTrigger();
21846         if(!this.width){
21847             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
21848         }
21849     },
21850
21851     // private
21852     initTrigger : function(){
21853         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
21854         this.trigger.addClassOnOver('x-form-trigger-over');
21855         this.trigger.addClassOnClick('x-form-trigger-click');
21856     },
21857
21858     // private
21859     onDestroy : function(){
21860         if(this.trigger){
21861             this.trigger.removeAllListeners();
21862             this.trigger.remove();
21863         }
21864         if(this.wrap){
21865             this.wrap.remove();
21866         }
21867         Roo.form.TriggerField.superclass.onDestroy.call(this);
21868     },
21869
21870     // private
21871     onFocus : function(){
21872         Roo.form.TriggerField.superclass.onFocus.call(this);
21873         if(!this.mimicing){
21874             this.wrap.addClass('x-trigger-wrap-focus');
21875             this.mimicing = true;
21876             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
21877             if(this.monitorTab){
21878                 this.el.on("keydown", this.checkTab, this);
21879             }
21880         }
21881     },
21882
21883     // private
21884     checkTab : function(e){
21885         if(e.getKey() == e.TAB){
21886             this.triggerBlur();
21887         }
21888     },
21889
21890     // private
21891     onBlur : function(){
21892         // do nothing
21893     },
21894
21895     // private
21896     mimicBlur : function(e, t){
21897         if(!this.wrap.contains(t) && this.validateBlur()){
21898             this.triggerBlur();
21899         }
21900     },
21901
21902     // private
21903     triggerBlur : function(){
21904         this.mimicing = false;
21905         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
21906         if(this.monitorTab){
21907             this.el.un("keydown", this.checkTab, this);
21908         }
21909         this.wrap.removeClass('x-trigger-wrap-focus');
21910         Roo.form.TriggerField.superclass.onBlur.call(this);
21911     },
21912
21913     // private
21914     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
21915     validateBlur : function(e, t){
21916         return true;
21917     },
21918
21919     // private
21920     onDisable : function(){
21921         Roo.form.TriggerField.superclass.onDisable.call(this);
21922         if(this.wrap){
21923             this.wrap.addClass('x-item-disabled');
21924         }
21925     },
21926
21927     // private
21928     onEnable : function(){
21929         Roo.form.TriggerField.superclass.onEnable.call(this);
21930         if(this.wrap){
21931             this.wrap.removeClass('x-item-disabled');
21932         }
21933     },
21934
21935     // private
21936     onShow : function(){
21937         var ae = this.getActionEl();
21938         
21939         if(ae){
21940             ae.dom.style.display = '';
21941             ae.dom.style.visibility = 'visible';
21942         }
21943     },
21944
21945     // private
21946     
21947     onHide : function(){
21948         var ae = this.getActionEl();
21949         ae.dom.style.display = 'none';
21950     },
21951
21952     /**
21953      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
21954      * by an implementing function.
21955      * @method
21956      * @param {EventObject} e
21957      */
21958     onTriggerClick : Roo.emptyFn
21959 });
21960
21961 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
21962 // to be extended by an implementing class.  For an example of implementing this class, see the custom
21963 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
21964 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
21965     initComponent : function(){
21966         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
21967
21968         this.triggerConfig = {
21969             tag:'span', cls:'x-form-twin-triggers', cn:[
21970             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
21971             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
21972         ]};
21973     },
21974
21975     getTrigger : function(index){
21976         return this.triggers[index];
21977     },
21978
21979     initTrigger : function(){
21980         var ts = this.trigger.select('.x-form-trigger', true);
21981         this.wrap.setStyle('overflow', 'hidden');
21982         var triggerField = this;
21983         ts.each(function(t, all, index){
21984             t.hide = function(){
21985                 var w = triggerField.wrap.getWidth();
21986                 this.dom.style.display = 'none';
21987                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21988             };
21989             t.show = function(){
21990                 var w = triggerField.wrap.getWidth();
21991                 this.dom.style.display = '';
21992                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21993             };
21994             var triggerIndex = 'Trigger'+(index+1);
21995
21996             if(this['hide'+triggerIndex]){
21997                 t.dom.style.display = 'none';
21998             }
21999             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
22000             t.addClassOnOver('x-form-trigger-over');
22001             t.addClassOnClick('x-form-trigger-click');
22002         }, this);
22003         this.triggers = ts.elements;
22004     },
22005
22006     onTrigger1Click : Roo.emptyFn,
22007     onTrigger2Click : Roo.emptyFn
22008 });/*
22009  * Based on:
22010  * Ext JS Library 1.1.1
22011  * Copyright(c) 2006-2007, Ext JS, LLC.
22012  *
22013  * Originally Released Under LGPL - original licence link has changed is not relivant.
22014  *
22015  * Fork - LGPL
22016  * <script type="text/javascript">
22017  */
22018  
22019 /**
22020  * @class Roo.form.TextArea
22021  * @extends Roo.form.TextField
22022  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
22023  * support for auto-sizing.
22024  * @constructor
22025  * Creates a new TextArea
22026  * @param {Object} config Configuration options
22027  */
22028 Roo.form.TextArea = function(config){
22029     Roo.form.TextArea.superclass.constructor.call(this, config);
22030     // these are provided exchanges for backwards compat
22031     // minHeight/maxHeight were replaced by growMin/growMax to be
22032     // compatible with TextField growing config values
22033     if(this.minHeight !== undefined){
22034         this.growMin = this.minHeight;
22035     }
22036     if(this.maxHeight !== undefined){
22037         this.growMax = this.maxHeight;
22038     }
22039 };
22040
22041 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
22042     /**
22043      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
22044      */
22045     growMin : 60,
22046     /**
22047      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
22048      */
22049     growMax: 1000,
22050     /**
22051      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
22052      * in the field (equivalent to setting overflow: hidden, defaults to false)
22053      */
22054     preventScrollbars: false,
22055     /**
22056      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22057      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
22058      */
22059
22060     // private
22061     onRender : function(ct, position){
22062         if(!this.el){
22063             this.defaultAutoCreate = {
22064                 tag: "textarea",
22065                 style:"width:300px;height:60px;",
22066                 autocomplete: "off"
22067             };
22068         }
22069         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
22070         if(this.grow){
22071             this.textSizeEl = Roo.DomHelper.append(document.body, {
22072                 tag: "pre", cls: "x-form-grow-sizer"
22073             });
22074             if(this.preventScrollbars){
22075                 this.el.setStyle("overflow", "hidden");
22076             }
22077             this.el.setHeight(this.growMin);
22078         }
22079     },
22080
22081     onDestroy : function(){
22082         if(this.textSizeEl){
22083             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
22084         }
22085         Roo.form.TextArea.superclass.onDestroy.call(this);
22086     },
22087
22088     // private
22089     onKeyUp : function(e){
22090         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
22091             this.autoSize();
22092         }
22093     },
22094
22095     /**
22096      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
22097      * This only takes effect if grow = true, and fires the autosize event if the height changes.
22098      */
22099     autoSize : function(){
22100         if(!this.grow || !this.textSizeEl){
22101             return;
22102         }
22103         var el = this.el;
22104         var v = el.dom.value;
22105         var ts = this.textSizeEl;
22106
22107         ts.innerHTML = '';
22108         ts.appendChild(document.createTextNode(v));
22109         v = ts.innerHTML;
22110
22111         Roo.fly(ts).setWidth(this.el.getWidth());
22112         if(v.length < 1){
22113             v = "&#160;&#160;";
22114         }else{
22115             if(Roo.isIE){
22116                 v = v.replace(/\n/g, '<p>&#160;</p>');
22117             }
22118             v += "&#160;\n&#160;";
22119         }
22120         ts.innerHTML = v;
22121         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
22122         if(h != this.lastHeight){
22123             this.lastHeight = h;
22124             this.el.setHeight(h);
22125             this.fireEvent("autosize", this, h);
22126         }
22127     }
22128 });/*
22129  * Based on:
22130  * Ext JS Library 1.1.1
22131  * Copyright(c) 2006-2007, Ext JS, LLC.
22132  *
22133  * Originally Released Under LGPL - original licence link has changed is not relivant.
22134  *
22135  * Fork - LGPL
22136  * <script type="text/javascript">
22137  */
22138  
22139
22140 /**
22141  * @class Roo.form.NumberField
22142  * @extends Roo.form.TextField
22143  * Numeric text field that provides automatic keystroke filtering and numeric validation.
22144  * @constructor
22145  * Creates a new NumberField
22146  * @param {Object} config Configuration options
22147  */
22148 Roo.form.NumberField = function(config){
22149     Roo.form.NumberField.superclass.constructor.call(this, config);
22150 };
22151
22152 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
22153     /**
22154      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22155      */
22156     fieldClass: "x-form-field x-form-num-field",
22157     /**
22158      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22159      */
22160     allowDecimals : true,
22161     /**
22162      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22163      */
22164     decimalSeparator : ".",
22165     /**
22166      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22167      */
22168     decimalPrecision : 2,
22169     /**
22170      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22171      */
22172     allowNegative : true,
22173     /**
22174      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22175      */
22176     minValue : Number.NEGATIVE_INFINITY,
22177     /**
22178      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22179      */
22180     maxValue : Number.MAX_VALUE,
22181     /**
22182      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22183      */
22184     minText : "The minimum value for this field is {0}",
22185     /**
22186      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22187      */
22188     maxText : "The maximum value for this field is {0}",
22189     /**
22190      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22191      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22192      */
22193     nanText : "{0} is not a valid number",
22194
22195     // private
22196     initEvents : function(){
22197         Roo.form.NumberField.superclass.initEvents.call(this);
22198         var allowed = "0123456789";
22199         if(this.allowDecimals){
22200             allowed += this.decimalSeparator;
22201         }
22202         if(this.allowNegative){
22203             allowed += "-";
22204         }
22205         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22206         var keyPress = function(e){
22207             var k = e.getKey();
22208             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22209                 return;
22210             }
22211             var c = e.getCharCode();
22212             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22213                 e.stopEvent();
22214             }
22215         };
22216         this.el.on("keypress", keyPress, this);
22217     },
22218
22219     // private
22220     validateValue : function(value){
22221         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22222             return false;
22223         }
22224         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22225              return true;
22226         }
22227         var num = this.parseValue(value);
22228         if(isNaN(num)){
22229             this.markInvalid(String.format(this.nanText, value));
22230             return false;
22231         }
22232         if(num < this.minValue){
22233             this.markInvalid(String.format(this.minText, this.minValue));
22234             return false;
22235         }
22236         if(num > this.maxValue){
22237             this.markInvalid(String.format(this.maxText, this.maxValue));
22238             return false;
22239         }
22240         return true;
22241     },
22242
22243     getValue : function(){
22244         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22245     },
22246
22247     // private
22248     parseValue : function(value){
22249         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22250         return isNaN(value) ? '' : value;
22251     },
22252
22253     // private
22254     fixPrecision : function(value){
22255         var nan = isNaN(value);
22256         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22257             return nan ? '' : value;
22258         }
22259         return parseFloat(value).toFixed(this.decimalPrecision);
22260     },
22261
22262     setValue : function(v){
22263         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22264     },
22265
22266     // private
22267     decimalPrecisionFcn : function(v){
22268         return Math.floor(v);
22269     },
22270
22271     beforeBlur : function(){
22272         var v = this.parseValue(this.getRawValue());
22273         if(v){
22274             this.setValue(this.fixPrecision(v));
22275         }
22276     }
22277 });/*
22278  * Based on:
22279  * Ext JS Library 1.1.1
22280  * Copyright(c) 2006-2007, Ext JS, LLC.
22281  *
22282  * Originally Released Under LGPL - original licence link has changed is not relivant.
22283  *
22284  * Fork - LGPL
22285  * <script type="text/javascript">
22286  */
22287  
22288 /**
22289  * @class Roo.form.DateField
22290  * @extends Roo.form.TriggerField
22291  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22292 * @constructor
22293 * Create a new DateField
22294 * @param {Object} config
22295  */
22296 Roo.form.DateField = function(config){
22297     Roo.form.DateField.superclass.constructor.call(this, config);
22298     
22299       this.addEvents({
22300          
22301         /**
22302          * @event select
22303          * Fires when a date is selected
22304              * @param {Roo.form.DateField} combo This combo box
22305              * @param {Date} date The date selected
22306              */
22307         'select' : true
22308          
22309     });
22310     
22311     
22312     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22313     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22314     this.ddMatch = null;
22315     if(this.disabledDates){
22316         var dd = this.disabledDates;
22317         var re = "(?:";
22318         for(var i = 0; i < dd.length; i++){
22319             re += dd[i];
22320             if(i != dd.length-1) re += "|";
22321         }
22322         this.ddMatch = new RegExp(re + ")");
22323     }
22324 };
22325
22326 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22327     /**
22328      * @cfg {String} format
22329      * The default date format string which can be overriden for localization support.  The format must be
22330      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22331      */
22332     format : "m/d/y",
22333     /**
22334      * @cfg {String} altFormats
22335      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22336      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22337      */
22338     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22339     /**
22340      * @cfg {Array} disabledDays
22341      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22342      */
22343     disabledDays : null,
22344     /**
22345      * @cfg {String} disabledDaysText
22346      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22347      */
22348     disabledDaysText : "Disabled",
22349     /**
22350      * @cfg {Array} disabledDates
22351      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22352      * expression so they are very powerful. Some examples:
22353      * <ul>
22354      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22355      * <li>["03/08", "09/16"] would disable those days for every year</li>
22356      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22357      * <li>["03/../2006"] would disable every day in March 2006</li>
22358      * <li>["^03"] would disable every day in every March</li>
22359      * </ul>
22360      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22361      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22362      */
22363     disabledDates : null,
22364     /**
22365      * @cfg {String} disabledDatesText
22366      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22367      */
22368     disabledDatesText : "Disabled",
22369     /**
22370      * @cfg {Date/String} minValue
22371      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22372      * valid format (defaults to null).
22373      */
22374     minValue : null,
22375     /**
22376      * @cfg {Date/String} maxValue
22377      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22378      * valid format (defaults to null).
22379      */
22380     maxValue : null,
22381     /**
22382      * @cfg {String} minText
22383      * The error text to display when the date in the cell is before minValue (defaults to
22384      * 'The date in this field must be after {minValue}').
22385      */
22386     minText : "The date in this field must be equal to or after {0}",
22387     /**
22388      * @cfg {String} maxText
22389      * The error text to display when the date in the cell is after maxValue (defaults to
22390      * 'The date in this field must be before {maxValue}').
22391      */
22392     maxText : "The date in this field must be equal to or before {0}",
22393     /**
22394      * @cfg {String} invalidText
22395      * The error text to display when the date in the field is invalid (defaults to
22396      * '{value} is not a valid date - it must be in the format {format}').
22397      */
22398     invalidText : "{0} is not a valid date - it must be in the format {1}",
22399     /**
22400      * @cfg {String} triggerClass
22401      * An additional CSS class used to style the trigger button.  The trigger will always get the
22402      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22403      * which displays a calendar icon).
22404      */
22405     triggerClass : 'x-form-date-trigger',
22406     
22407
22408     /**
22409      * @cfg {bool} useIso
22410      * if enabled, then the date field will use a hidden field to store the 
22411      * real value as iso formated date. default (false)
22412      */ 
22413     useIso : false,
22414     /**
22415      * @cfg {String/Object} autoCreate
22416      * A DomHelper element spec, or true for a default element spec (defaults to
22417      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22418      */ 
22419     // private
22420     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22421     
22422     // private
22423     hiddenField: false,
22424     
22425     onRender : function(ct, position)
22426     {
22427         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22428         if (this.useIso) {
22429             this.el.dom.removeAttribute('name'); 
22430             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22431                     'before', true);
22432             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
22433             // prevent input submission
22434             this.hiddenName = this.name;
22435         }
22436             
22437             
22438     },
22439     
22440     // private
22441     validateValue : function(value)
22442     {
22443         value = this.formatDate(value);
22444         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22445             return false;
22446         }
22447         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22448              return true;
22449         }
22450         var svalue = value;
22451         value = this.parseDate(value);
22452         if(!value){
22453             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22454             return false;
22455         }
22456         var time = value.getTime();
22457         if(this.minValue && time < this.minValue.getTime()){
22458             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22459             return false;
22460         }
22461         if(this.maxValue && time > this.maxValue.getTime()){
22462             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22463             return false;
22464         }
22465         if(this.disabledDays){
22466             var day = value.getDay();
22467             for(var i = 0; i < this.disabledDays.length; i++) {
22468                 if(day === this.disabledDays[i]){
22469                     this.markInvalid(this.disabledDaysText);
22470                     return false;
22471                 }
22472             }
22473         }
22474         var fvalue = this.formatDate(value);
22475         if(this.ddMatch && this.ddMatch.test(fvalue)){
22476             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22477             return false;
22478         }
22479         return true;
22480     },
22481
22482     // private
22483     // Provides logic to override the default TriggerField.validateBlur which just returns true
22484     validateBlur : function(){
22485         return !this.menu || !this.menu.isVisible();
22486     },
22487
22488     /**
22489      * Returns the current date value of the date field.
22490      * @return {Date} The date value
22491      */
22492     getValue : function(){
22493         
22494         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22495     },
22496
22497     /**
22498      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22499      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22500      * (the default format used is "m/d/y").
22501      * <br />Usage:
22502      * <pre><code>
22503 //All of these calls set the same date value (May 4, 2006)
22504
22505 //Pass a date object:
22506 var dt = new Date('5/4/06');
22507 dateField.setValue(dt);
22508
22509 //Pass a date string (default format):
22510 dateField.setValue('5/4/06');
22511
22512 //Pass a date string (custom format):
22513 dateField.format = 'Y-m-d';
22514 dateField.setValue('2006-5-4');
22515 </code></pre>
22516      * @param {String/Date} date The date or valid date string
22517      */
22518     setValue : function(date){
22519         if (this.hiddenField) {
22520             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22521         }
22522         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22523     },
22524
22525     // private
22526     parseDate : function(value){
22527         if(!value || value instanceof Date){
22528             return value;
22529         }
22530         var v = Date.parseDate(value, this.format);
22531         if(!v && this.altFormats){
22532             if(!this.altFormatsArray){
22533                 this.altFormatsArray = this.altFormats.split("|");
22534             }
22535             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22536                 v = Date.parseDate(value, this.altFormatsArray[i]);
22537             }
22538         }
22539         return v;
22540     },
22541
22542     // private
22543     formatDate : function(date, fmt){
22544         return (!date || !(date instanceof Date)) ?
22545                date : date.dateFormat(fmt || this.format);
22546     },
22547
22548     // private
22549     menuListeners : {
22550         select: function(m, d){
22551             this.setValue(d);
22552             this.fireEvent('select', this, d);
22553         },
22554         show : function(){ // retain focus styling
22555             this.onFocus();
22556         },
22557         hide : function(){
22558             this.focus.defer(10, this);
22559             var ml = this.menuListeners;
22560             this.menu.un("select", ml.select,  this);
22561             this.menu.un("show", ml.show,  this);
22562             this.menu.un("hide", ml.hide,  this);
22563         }
22564     },
22565
22566     // private
22567     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22568     onTriggerClick : function(){
22569         if(this.disabled){
22570             return;
22571         }
22572         if(this.menu == null){
22573             this.menu = new Roo.menu.DateMenu();
22574         }
22575         Roo.apply(this.menu.picker,  {
22576             showClear: this.allowBlank,
22577             minDate : this.minValue,
22578             maxDate : this.maxValue,
22579             disabledDatesRE : this.ddMatch,
22580             disabledDatesText : this.disabledDatesText,
22581             disabledDays : this.disabledDays,
22582             disabledDaysText : this.disabledDaysText,
22583             format : this.format,
22584             minText : String.format(this.minText, this.formatDate(this.minValue)),
22585             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22586         });
22587         this.menu.on(Roo.apply({}, this.menuListeners, {
22588             scope:this
22589         }));
22590         this.menu.picker.setValue(this.getValue() || new Date());
22591         this.menu.show(this.el, "tl-bl?");
22592     },
22593
22594     beforeBlur : function(){
22595         var v = this.parseDate(this.getRawValue());
22596         if(v){
22597             this.setValue(v);
22598         }
22599     }
22600
22601     /** @cfg {Boolean} grow @hide */
22602     /** @cfg {Number} growMin @hide */
22603     /** @cfg {Number} growMax @hide */
22604     /**
22605      * @hide
22606      * @method autoSize
22607      */
22608 });/*
22609  * Based on:
22610  * Ext JS Library 1.1.1
22611  * Copyright(c) 2006-2007, Ext JS, LLC.
22612  *
22613  * Originally Released Under LGPL - original licence link has changed is not relivant.
22614  *
22615  * Fork - LGPL
22616  * <script type="text/javascript">
22617  */
22618  
22619
22620 /**
22621  * @class Roo.form.ComboBox
22622  * @extends Roo.form.TriggerField
22623  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22624  * @constructor
22625  * Create a new ComboBox.
22626  * @param {Object} config Configuration options
22627  */
22628 Roo.form.ComboBox = function(config){
22629     Roo.form.ComboBox.superclass.constructor.call(this, config);
22630     this.addEvents({
22631         /**
22632          * @event expand
22633          * Fires when the dropdown list is expanded
22634              * @param {Roo.form.ComboBox} combo This combo box
22635              */
22636         'expand' : true,
22637         /**
22638          * @event collapse
22639          * Fires when the dropdown list is collapsed
22640              * @param {Roo.form.ComboBox} combo This combo box
22641              */
22642         'collapse' : true,
22643         /**
22644          * @event beforeselect
22645          * Fires before a list item is selected. Return false to cancel the selection.
22646              * @param {Roo.form.ComboBox} combo This combo box
22647              * @param {Roo.data.Record} record The data record returned from the underlying store
22648              * @param {Number} index The index of the selected item in the dropdown list
22649              */
22650         'beforeselect' : true,
22651         /**
22652          * @event select
22653          * Fires when a list item is selected
22654              * @param {Roo.form.ComboBox} combo This combo box
22655              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22656              * @param {Number} index The index of the selected item in the dropdown list
22657              */
22658         'select' : true,
22659         /**
22660          * @event beforequery
22661          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22662          * The event object passed has these properties:
22663              * @param {Roo.form.ComboBox} combo This combo box
22664              * @param {String} query The query
22665              * @param {Boolean} forceAll true to force "all" query
22666              * @param {Boolean} cancel true to cancel the query
22667              * @param {Object} e The query event object
22668              */
22669         'beforequery': true,
22670          /**
22671          * @event add
22672          * Fires when the 'add' icon is pressed (add a listener to enable add button)
22673              * @param {Roo.form.ComboBox} combo This combo box
22674              */
22675         'add' : true,
22676         /**
22677          * @event edit
22678          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
22679              * @param {Roo.form.ComboBox} combo This combo box
22680              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
22681              */
22682         'edit' : true
22683         
22684         
22685     });
22686     if(this.transform){
22687         this.allowDomMove = false;
22688         var s = Roo.getDom(this.transform);
22689         if(!this.hiddenName){
22690             this.hiddenName = s.name;
22691         }
22692         if(!this.store){
22693             this.mode = 'local';
22694             var d = [], opts = s.options;
22695             for(var i = 0, len = opts.length;i < len; i++){
22696                 var o = opts[i];
22697                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22698                 if(o.selected) {
22699                     this.value = value;
22700                 }
22701                 d.push([value, o.text]);
22702             }
22703             this.store = new Roo.data.SimpleStore({
22704                 'id': 0,
22705                 fields: ['value', 'text'],
22706                 data : d
22707             });
22708             this.valueField = 'value';
22709             this.displayField = 'text';
22710         }
22711         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22712         if(!this.lazyRender){
22713             this.target = true;
22714             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22715             s.parentNode.removeChild(s); // remove it
22716             this.render(this.el.parentNode);
22717         }else{
22718             s.parentNode.removeChild(s); // remove it
22719         }
22720
22721     }
22722     if (this.store) {
22723         this.store = Roo.factory(this.store, Roo.data);
22724     }
22725     
22726     this.selectedIndex = -1;
22727     if(this.mode == 'local'){
22728         if(config.queryDelay === undefined){
22729             this.queryDelay = 10;
22730         }
22731         if(config.minChars === undefined){
22732             this.minChars = 0;
22733         }
22734     }
22735 };
22736
22737 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22738     /**
22739      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22740      */
22741     /**
22742      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
22743      * rendering into an Roo.Editor, defaults to false)
22744      */
22745     /**
22746      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
22747      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
22748      */
22749     /**
22750      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
22751      */
22752     /**
22753      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
22754      * the dropdown list (defaults to undefined, with no header element)
22755      */
22756
22757      /**
22758      * @cfg {String/Roo.Template} tpl The template to use to render the output
22759      */
22760      
22761     // private
22762     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
22763     /**
22764      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
22765      */
22766     listWidth: undefined,
22767     /**
22768      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
22769      * mode = 'remote' or 'text' if mode = 'local')
22770      */
22771     displayField: undefined,
22772     /**
22773      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
22774      * mode = 'remote' or 'value' if mode = 'local'). 
22775      * Note: use of a valueField requires the user make a selection
22776      * in order for a value to be mapped.
22777      */
22778     valueField: undefined,
22779     /**
22780      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
22781      * field's data value (defaults to the underlying DOM element's name)
22782      */
22783     hiddenName: undefined,
22784     /**
22785      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
22786      */
22787     listClass: '',
22788     /**
22789      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
22790      */
22791     selectedClass: 'x-combo-selected',
22792     /**
22793      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22794      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
22795      * which displays a downward arrow icon).
22796      */
22797     triggerClass : 'x-form-arrow-trigger',
22798     /**
22799      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
22800      */
22801     shadow:'sides',
22802     /**
22803      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
22804      * anchor positions (defaults to 'tl-bl')
22805      */
22806     listAlign: 'tl-bl?',
22807     /**
22808      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
22809      */
22810     maxHeight: 300,
22811     /**
22812      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
22813      * query specified by the allQuery config option (defaults to 'query')
22814      */
22815     triggerAction: 'query',
22816     /**
22817      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
22818      * (defaults to 4, does not apply if editable = false)
22819      */
22820     minChars : 4,
22821     /**
22822      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
22823      * delay (typeAheadDelay) if it matches a known value (defaults to false)
22824      */
22825     typeAhead: false,
22826     /**
22827      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
22828      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
22829      */
22830     queryDelay: 500,
22831     /**
22832      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
22833      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
22834      */
22835     pageSize: 0,
22836     /**
22837      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
22838      * when editable = true (defaults to false)
22839      */
22840     selectOnFocus:false,
22841     /**
22842      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
22843      */
22844     queryParam: 'query',
22845     /**
22846      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
22847      * when mode = 'remote' (defaults to 'Loading...')
22848      */
22849     loadingText: 'Loading...',
22850     /**
22851      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
22852      */
22853     resizable: false,
22854     /**
22855      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
22856      */
22857     handleHeight : 8,
22858     /**
22859      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
22860      * traditional select (defaults to true)
22861      */
22862     editable: true,
22863     /**
22864      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
22865      */
22866     allQuery: '',
22867     /**
22868      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
22869      */
22870     mode: 'remote',
22871     /**
22872      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
22873      * listWidth has a higher value)
22874      */
22875     minListWidth : 70,
22876     /**
22877      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
22878      * allow the user to set arbitrary text into the field (defaults to false)
22879      */
22880     forceSelection:false,
22881     /**
22882      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
22883      * if typeAhead = true (defaults to 250)
22884      */
22885     typeAheadDelay : 250,
22886     /**
22887      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
22888      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
22889      */
22890     valueNotFoundText : undefined,
22891     /**
22892      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
22893      */
22894     blockFocus : false,
22895     
22896     /**
22897      * @cfg {Boolean} disableClear Disable showing of clear button.
22898      */
22899     disableClear : false,
22900     /**
22901      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
22902      */
22903     alwaysQuery : false,
22904     
22905     //private
22906     addicon : false,
22907     editicon: false,
22908     
22909     
22910     // private
22911     onRender : function(ct, position){
22912         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
22913         if(this.hiddenName){
22914             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
22915                     'before', true);
22916             this.hiddenField.value =
22917                 this.hiddenValue !== undefined ? this.hiddenValue :
22918                 this.value !== undefined ? this.value : '';
22919
22920             // prevent input submission
22921             this.el.dom.removeAttribute('name');
22922         }
22923         if(Roo.isGecko){
22924             this.el.dom.setAttribute('autocomplete', 'off');
22925         }
22926
22927         var cls = 'x-combo-list';
22928
22929         this.list = new Roo.Layer({
22930             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
22931         });
22932
22933         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
22934         this.list.setWidth(lw);
22935         this.list.swallowEvent('mousewheel');
22936         this.assetHeight = 0;
22937
22938         if(this.title){
22939             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
22940             this.assetHeight += this.header.getHeight();
22941         }
22942
22943         this.innerList = this.list.createChild({cls:cls+'-inner'});
22944         this.innerList.on('mouseover', this.onViewOver, this);
22945         this.innerList.on('mousemove', this.onViewMove, this);
22946         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
22947         
22948         if(this.allowBlank && !this.pageSize && !this.disableClear){
22949             this.footer = this.list.createChild({cls:cls+'-ft'});
22950             this.pageTb = new Roo.Toolbar(this.footer);
22951            
22952         }
22953         if(this.pageSize){
22954             this.footer = this.list.createChild({cls:cls+'-ft'});
22955             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
22956                     {pageSize: this.pageSize});
22957             
22958         }
22959         
22960         if (this.pageTb && this.allowBlank && !this.disableClear) {
22961             var _this = this;
22962             this.pageTb.add(new Roo.Toolbar.Fill(), {
22963                 cls: 'x-btn-icon x-btn-clear',
22964                 text: '&#160;',
22965                 handler: function()
22966                 {
22967                     _this.collapse();
22968                     _this.clearValue();
22969                     _this.onSelect(false, -1);
22970                 }
22971             });
22972         }
22973         if (this.footer) {
22974             this.assetHeight += this.footer.getHeight();
22975         }
22976         
22977
22978         if(!this.tpl){
22979             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
22980         }
22981
22982         this.view = new Roo.View(this.innerList, this.tpl, {
22983             singleSelect:true, store: this.store, selectedClass: this.selectedClass
22984         });
22985
22986         this.view.on('click', this.onViewClick, this);
22987
22988         this.store.on('beforeload', this.onBeforeLoad, this);
22989         this.store.on('load', this.onLoad, this);
22990         this.store.on('loadexception', this.collapse, this);
22991
22992         if(this.resizable){
22993             this.resizer = new Roo.Resizable(this.list,  {
22994                pinned:true, handles:'se'
22995             });
22996             this.resizer.on('resize', function(r, w, h){
22997                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
22998                 this.listWidth = w;
22999                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23000                 this.restrictHeight();
23001             }, this);
23002             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23003         }
23004         if(!this.editable){
23005             this.editable = true;
23006             this.setEditable(false);
23007         }  
23008         
23009         
23010         if (typeof(this.events.add.listeners) != 'undefined') {
23011             
23012             this.addicon = this.wrap.createChild(
23013                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
23014        
23015             this.addicon.on('click', function(e) {
23016                 this.fireEvent('add', this);
23017             }, this);
23018         }
23019         if (typeof(this.events.edit.listeners) != 'undefined') {
23020             
23021             this.editicon = this.wrap.createChild(
23022                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
23023             if (this.addicon) {
23024                 this.editicon.setStyle('margin-left', '40px');
23025             }
23026             this.editicon.on('click', function(e) {
23027                 
23028                 // we fire even  if inothing is selected..
23029                 this.fireEvent('edit', this, this.lastData );
23030                 
23031             }, this);
23032         }
23033         
23034         
23035         
23036     },
23037
23038     // private
23039     initEvents : function(){
23040         Roo.form.ComboBox.superclass.initEvents.call(this);
23041
23042         this.keyNav = new Roo.KeyNav(this.el, {
23043             "up" : function(e){
23044                 this.inKeyMode = true;
23045                 this.selectPrev();
23046             },
23047
23048             "down" : function(e){
23049                 if(!this.isExpanded()){
23050                     this.onTriggerClick();
23051                 }else{
23052                     this.inKeyMode = true;
23053                     this.selectNext();
23054                 }
23055             },
23056
23057             "enter" : function(e){
23058                 this.onViewClick();
23059                 //return true;
23060             },
23061
23062             "esc" : function(e){
23063                 this.collapse();
23064             },
23065
23066             "tab" : function(e){
23067                 this.onViewClick(false);
23068                 return true;
23069             },
23070
23071             scope : this,
23072
23073             doRelay : function(foo, bar, hname){
23074                 if(hname == 'down' || this.scope.isExpanded()){
23075                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23076                 }
23077                 return true;
23078             },
23079
23080             forceKeyDown: true
23081         });
23082         this.queryDelay = Math.max(this.queryDelay || 10,
23083                 this.mode == 'local' ? 10 : 250);
23084         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23085         if(this.typeAhead){
23086             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23087         }
23088         if(this.editable !== false){
23089             this.el.on("keyup", this.onKeyUp, this);
23090         }
23091         if(this.forceSelection){
23092             this.on('blur', this.doForce, this);
23093         }
23094     },
23095
23096     onDestroy : function(){
23097         if(this.view){
23098             this.view.setStore(null);
23099             this.view.el.removeAllListeners();
23100             this.view.el.remove();
23101             this.view.purgeListeners();
23102         }
23103         if(this.list){
23104             this.list.destroy();
23105         }
23106         if(this.store){
23107             this.store.un('beforeload', this.onBeforeLoad, this);
23108             this.store.un('load', this.onLoad, this);
23109             this.store.un('loadexception', this.collapse, this);
23110         }
23111         Roo.form.ComboBox.superclass.onDestroy.call(this);
23112     },
23113
23114     // private
23115     fireKey : function(e){
23116         if(e.isNavKeyPress() && !this.list.isVisible()){
23117             this.fireEvent("specialkey", this, e);
23118         }
23119     },
23120
23121     // private
23122     onResize: function(w, h){
23123         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23124         
23125         if(typeof w != 'number'){
23126             // we do not handle it!?!?
23127             return;
23128         }
23129         var tw = this.trigger.getWidth();
23130         tw += this.addicon ? this.addicon.getWidth() : 0;
23131         tw += this.editicon ? this.editicon.getWidth() : 0;
23132         var x = w - tw;
23133         this.el.setWidth( this.adjustWidth('input', x));
23134             
23135         this.trigger.setStyle('left', x+'px');
23136         
23137         if(this.list && this.listWidth === undefined){
23138             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23139             this.list.setWidth(lw);
23140             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23141         }
23142         
23143     
23144         
23145     },
23146
23147     /**
23148      * Allow or prevent the user from directly editing the field text.  If false is passed,
23149      * the user will only be able to select from the items defined in the dropdown list.  This method
23150      * is the runtime equivalent of setting the 'editable' config option at config time.
23151      * @param {Boolean} value True to allow the user to directly edit the field text
23152      */
23153     setEditable : function(value){
23154         if(value == this.editable){
23155             return;
23156         }
23157         this.editable = value;
23158         if(!value){
23159             this.el.dom.setAttribute('readOnly', true);
23160             this.el.on('mousedown', this.onTriggerClick,  this);
23161             this.el.addClass('x-combo-noedit');
23162         }else{
23163             this.el.dom.setAttribute('readOnly', false);
23164             this.el.un('mousedown', this.onTriggerClick,  this);
23165             this.el.removeClass('x-combo-noedit');
23166         }
23167     },
23168
23169     // private
23170     onBeforeLoad : function(){
23171         if(!this.hasFocus){
23172             return;
23173         }
23174         this.innerList.update(this.loadingText ?
23175                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23176         this.restrictHeight();
23177         this.selectedIndex = -1;
23178     },
23179
23180     // private
23181     onLoad : function(){
23182         if(!this.hasFocus){
23183             return;
23184         }
23185         if(this.store.getCount() > 0){
23186             this.expand();
23187             this.restrictHeight();
23188             if(this.lastQuery == this.allQuery){
23189                 if(this.editable){
23190                     this.el.dom.select();
23191                 }
23192                 if(!this.selectByValue(this.value, true)){
23193                     this.select(0, true);
23194                 }
23195             }else{
23196                 this.selectNext();
23197                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23198                     this.taTask.delay(this.typeAheadDelay);
23199                 }
23200             }
23201         }else{
23202             this.onEmptyResults();
23203         }
23204         //this.el.focus();
23205     },
23206
23207     // private
23208     onTypeAhead : function(){
23209         if(this.store.getCount() > 0){
23210             var r = this.store.getAt(0);
23211             var newValue = r.data[this.displayField];
23212             var len = newValue.length;
23213             var selStart = this.getRawValue().length;
23214             if(selStart != len){
23215                 this.setRawValue(newValue);
23216                 this.selectText(selStart, newValue.length);
23217             }
23218         }
23219     },
23220
23221     // private
23222     onSelect : function(record, index){
23223         if(this.fireEvent('beforeselect', this, record, index) !== false){
23224             this.setFromData(index > -1 ? record.data : false);
23225             this.collapse();
23226             this.fireEvent('select', this, record, index);
23227         }
23228     },
23229
23230     /**
23231      * Returns the currently selected field value or empty string if no value is set.
23232      * @return {String} value The selected value
23233      */
23234     getValue : function(){
23235         if(this.valueField){
23236             return typeof this.value != 'undefined' ? this.value : '';
23237         }else{
23238             return Roo.form.ComboBox.superclass.getValue.call(this);
23239         }
23240     },
23241
23242     /**
23243      * Clears any text/value currently set in the field
23244      */
23245     clearValue : function(){
23246         if(this.hiddenField){
23247             this.hiddenField.value = '';
23248         }
23249         this.value = '';
23250         this.setRawValue('');
23251         this.lastSelectionText = '';
23252         this.applyEmptyText();
23253     },
23254
23255     /**
23256      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23257      * will be displayed in the field.  If the value does not match the data value of an existing item,
23258      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23259      * Otherwise the field will be blank (although the value will still be set).
23260      * @param {String} value The value to match
23261      */
23262     setValue : function(v){
23263         var text = v;
23264         if(this.valueField){
23265             var r = this.findRecord(this.valueField, v);
23266             if(r){
23267                 text = r.data[this.displayField];
23268             }else if(this.valueNotFoundText !== undefined){
23269                 text = this.valueNotFoundText;
23270             }
23271         }
23272         this.lastSelectionText = text;
23273         if(this.hiddenField){
23274             this.hiddenField.value = v;
23275         }
23276         Roo.form.ComboBox.superclass.setValue.call(this, text);
23277         this.value = v;
23278     },
23279     /**
23280      * @property {Object} the last set data for the element
23281      */
23282     
23283     lastData : false,
23284     /**
23285      * Sets the value of the field based on a object which is related to the record format for the store.
23286      * @param {Object} value the value to set as. or false on reset?
23287      */
23288     setFromData : function(o){
23289         var dv = ''; // display value
23290         var vv = ''; // value value..
23291         this.lastData = o;
23292         if (this.displayField) {
23293             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23294         } else {
23295             // this is an error condition!!!
23296             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23297         }
23298         
23299         if(this.valueField){
23300             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23301         }
23302         if(this.hiddenField){
23303             this.hiddenField.value = vv;
23304             
23305             this.lastSelectionText = dv;
23306             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23307             this.value = vv;
23308             return;
23309         }
23310         // no hidden field.. - we store the value in 'value', but still display
23311         // display field!!!!
23312         this.lastSelectionText = dv;
23313         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23314         this.value = vv;
23315         
23316         
23317     },
23318     // private
23319     reset : function(){
23320         // overridden so that last data is reset..
23321         this.setValue(this.originalValue);
23322         this.clearInvalid();
23323         this.lastData = false;
23324     },
23325     // private
23326     findRecord : function(prop, value){
23327         var record;
23328         if(this.store.getCount() > 0){
23329             this.store.each(function(r){
23330                 if(r.data[prop] == value){
23331                     record = r;
23332                     return false;
23333                 }
23334             });
23335         }
23336         return record;
23337     },
23338
23339     // private
23340     onViewMove : function(e, t){
23341         this.inKeyMode = false;
23342     },
23343
23344     // private
23345     onViewOver : function(e, t){
23346         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23347             return;
23348         }
23349         var item = this.view.findItemFromChild(t);
23350         if(item){
23351             var index = this.view.indexOf(item);
23352             this.select(index, false);
23353         }
23354     },
23355
23356     // private
23357     onViewClick : function(doFocus){
23358         var index = this.view.getSelectedIndexes()[0];
23359         var r = this.store.getAt(index);
23360         if(r){
23361             this.onSelect(r, index);
23362         }
23363         if(doFocus !== false && !this.blockFocus){
23364             this.el.focus();
23365         }
23366     },
23367
23368     // private
23369     restrictHeight : function(){
23370         this.innerList.dom.style.height = '';
23371         var inner = this.innerList.dom;
23372         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23373         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23374         this.list.beginUpdate();
23375         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23376         this.list.alignTo(this.el, this.listAlign);
23377         this.list.endUpdate();
23378     },
23379
23380     // private
23381     onEmptyResults : function(){
23382         this.collapse();
23383     },
23384
23385     /**
23386      * Returns true if the dropdown list is expanded, else false.
23387      */
23388     isExpanded : function(){
23389         return this.list.isVisible();
23390     },
23391
23392     /**
23393      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23394      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23395      * @param {String} value The data value of the item to select
23396      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23397      * selected item if it is not currently in view (defaults to true)
23398      * @return {Boolean} True if the value matched an item in the list, else false
23399      */
23400     selectByValue : function(v, scrollIntoView){
23401         if(v !== undefined && v !== null){
23402             var r = this.findRecord(this.valueField || this.displayField, v);
23403             if(r){
23404                 this.select(this.store.indexOf(r), scrollIntoView);
23405                 return true;
23406             }
23407         }
23408         return false;
23409     },
23410
23411     /**
23412      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23413      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23414      * @param {Number} index The zero-based index of the list item to select
23415      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23416      * selected item if it is not currently in view (defaults to true)
23417      */
23418     select : function(index, scrollIntoView){
23419         this.selectedIndex = index;
23420         this.view.select(index);
23421         if(scrollIntoView !== false){
23422             var el = this.view.getNode(index);
23423             if(el){
23424                 this.innerList.scrollChildIntoView(el, false);
23425             }
23426         }
23427     },
23428
23429     // private
23430     selectNext : function(){
23431         var ct = this.store.getCount();
23432         if(ct > 0){
23433             if(this.selectedIndex == -1){
23434                 this.select(0);
23435             }else if(this.selectedIndex < ct-1){
23436                 this.select(this.selectedIndex+1);
23437             }
23438         }
23439     },
23440
23441     // private
23442     selectPrev : function(){
23443         var ct = this.store.getCount();
23444         if(ct > 0){
23445             if(this.selectedIndex == -1){
23446                 this.select(0);
23447             }else if(this.selectedIndex != 0){
23448                 this.select(this.selectedIndex-1);
23449             }
23450         }
23451     },
23452
23453     // private
23454     onKeyUp : function(e){
23455         if(this.editable !== false && !e.isSpecialKey()){
23456             this.lastKey = e.getKey();
23457             this.dqTask.delay(this.queryDelay);
23458         }
23459     },
23460
23461     // private
23462     validateBlur : function(){
23463         return !this.list || !this.list.isVisible();   
23464     },
23465
23466     // private
23467     initQuery : function(){
23468         this.doQuery(this.getRawValue());
23469     },
23470
23471     // private
23472     doForce : function(){
23473         if(this.el.dom.value.length > 0){
23474             this.el.dom.value =
23475                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23476             this.applyEmptyText();
23477         }
23478     },
23479
23480     /**
23481      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23482      * query allowing the query action to be canceled if needed.
23483      * @param {String} query The SQL query to execute
23484      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23485      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23486      * saved in the current store (defaults to false)
23487      */
23488     doQuery : function(q, forceAll){
23489         if(q === undefined || q === null){
23490             q = '';
23491         }
23492         var qe = {
23493             query: q,
23494             forceAll: forceAll,
23495             combo: this,
23496             cancel:false
23497         };
23498         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23499             return false;
23500         }
23501         q = qe.query;
23502         forceAll = qe.forceAll;
23503         if(forceAll === true || (q.length >= this.minChars)){
23504             if(this.lastQuery != q || this.alwaysQuery){
23505                 this.lastQuery = q;
23506                 if(this.mode == 'local'){
23507                     this.selectedIndex = -1;
23508                     if(forceAll){
23509                         this.store.clearFilter();
23510                     }else{
23511                         this.store.filter(this.displayField, q);
23512                     }
23513                     this.onLoad();
23514                 }else{
23515                     this.store.baseParams[this.queryParam] = q;
23516                     this.store.load({
23517                         params: this.getParams(q)
23518                     });
23519                     this.expand();
23520                 }
23521             }else{
23522                 this.selectedIndex = -1;
23523                 this.onLoad();   
23524             }
23525         }
23526     },
23527
23528     // private
23529     getParams : function(q){
23530         var p = {};
23531         //p[this.queryParam] = q;
23532         if(this.pageSize){
23533             p.start = 0;
23534             p.limit = this.pageSize;
23535         }
23536         return p;
23537     },
23538
23539     /**
23540      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23541      */
23542     collapse : function(){
23543         if(!this.isExpanded()){
23544             return;
23545         }
23546         this.list.hide();
23547         Roo.get(document).un('mousedown', this.collapseIf, this);
23548         Roo.get(document).un('mousewheel', this.collapseIf, this);
23549         if (!this.editable) {
23550             Roo.get(document).un('keydown', this.listKeyPress, this);
23551         }
23552         this.fireEvent('collapse', this);
23553     },
23554
23555     // private
23556     collapseIf : function(e){
23557         if(!e.within(this.wrap) && !e.within(this.list)){
23558             this.collapse();
23559         }
23560     },
23561
23562     /**
23563      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23564      */
23565     expand : function(){
23566         if(this.isExpanded() || !this.hasFocus){
23567             return;
23568         }
23569         this.list.alignTo(this.el, this.listAlign);
23570         this.list.show();
23571         Roo.get(document).on('mousedown', this.collapseIf, this);
23572         Roo.get(document).on('mousewheel', this.collapseIf, this);
23573         if (!this.editable) {
23574             Roo.get(document).on('keydown', this.listKeyPress, this);
23575         }
23576         
23577         this.fireEvent('expand', this);
23578     },
23579
23580     // private
23581     // Implements the default empty TriggerField.onTriggerClick function
23582     onTriggerClick : function(){
23583         if(this.disabled){
23584             return;
23585         }
23586         if(this.isExpanded()){
23587             this.collapse();
23588             if (!this.blockFocus) {
23589                 this.el.focus();
23590             }
23591             
23592         }else {
23593             this.hasFocus = true;
23594             if(this.triggerAction == 'all') {
23595                 this.doQuery(this.allQuery, true);
23596             } else {
23597                 this.doQuery(this.getRawValue());
23598             }
23599             if (!this.blockFocus) {
23600                 this.el.focus();
23601             }
23602         }
23603     },
23604     listKeyPress : function(e)
23605     {
23606         //Roo.log('listkeypress');
23607         // scroll to first matching element based on key pres..
23608         if (e.isSpecialKey()) {
23609             return false;
23610         }
23611         var k = String.fromCharCode(e.getKey()).toUpperCase();
23612         //Roo.log(k);
23613         var match  = false;
23614         var csel = this.view.getSelectedNodes();
23615         var cselitem = false;
23616         if (csel.length) {
23617             var ix = this.view.indexOf(csel[0]);
23618             cselitem  = this.store.getAt(ix);
23619             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
23620                 cselitem = false;
23621             }
23622             
23623         }
23624         
23625         this.store.each(function(v) { 
23626             if (cselitem) {
23627                 // start at existing selection.
23628                 if (cselitem.id == v.id) {
23629                     cselitem = false;
23630                 }
23631                 return;
23632             }
23633                 
23634             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
23635                 match = this.store.indexOf(v);
23636                 return false;
23637             }
23638         }, this);
23639         
23640         if (match === false) {
23641             return true; // no more action?
23642         }
23643         // scroll to?
23644         this.view.select(match);
23645         var sn = Roo.get(this.view.getSelectedNodes()[0])
23646         sn.scrollIntoView(sn.dom.parentNode, false);
23647     }
23648
23649     /** 
23650     * @cfg {Boolean} grow 
23651     * @hide 
23652     */
23653     /** 
23654     * @cfg {Number} growMin 
23655     * @hide 
23656     */
23657     /** 
23658     * @cfg {Number} growMax 
23659     * @hide 
23660     */
23661     /**
23662      * @hide
23663      * @method autoSize
23664      */
23665 });/*
23666  * Based on:
23667  * Ext JS Library 1.1.1
23668  * Copyright(c) 2006-2007, Ext JS, LLC.
23669  *
23670  * Originally Released Under LGPL - original licence link has changed is not relivant.
23671  *
23672  * Fork - LGPL
23673  * <script type="text/javascript">
23674  */
23675 /**
23676  * @class Roo.form.Checkbox
23677  * @extends Roo.form.Field
23678  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
23679  * @constructor
23680  * Creates a new Checkbox
23681  * @param {Object} config Configuration options
23682  */
23683 Roo.form.Checkbox = function(config){
23684     Roo.form.Checkbox.superclass.constructor.call(this, config);
23685     this.addEvents({
23686         /**
23687          * @event check
23688          * Fires when the checkbox is checked or unchecked.
23689              * @param {Roo.form.Checkbox} this This checkbox
23690              * @param {Boolean} checked The new checked value
23691              */
23692         check : true
23693     });
23694 };
23695
23696 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
23697     /**
23698      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
23699      */
23700     focusClass : undefined,
23701     /**
23702      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
23703      */
23704     fieldClass: "x-form-field",
23705     /**
23706      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
23707      */
23708     checked: false,
23709     /**
23710      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
23711      * {tag: "input", type: "checkbox", autocomplete: "off"})
23712      */
23713     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
23714     /**
23715      * @cfg {String} boxLabel The text that appears beside the checkbox
23716      */
23717     boxLabel : "",
23718     /**
23719      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
23720      */  
23721     inputValue : '1',
23722     /**
23723      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23724      */
23725      valueOff: '0', // value when not checked..
23726
23727     actionMode : 'viewEl', 
23728     //
23729     // private
23730     itemCls : 'x-menu-check-item x-form-item',
23731     groupClass : 'x-menu-group-item',
23732     inputType : 'hidden',
23733     
23734     
23735     inSetChecked: false, // check that we are not calling self...
23736     
23737     inputElement: false, // real input element?
23738     basedOn: false, // ????
23739     
23740     isFormField: true, // not sure where this is needed!!!!
23741
23742     onResize : function(){
23743         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
23744         if(!this.boxLabel){
23745             this.el.alignTo(this.wrap, 'c-c');
23746         }
23747     },
23748
23749     initEvents : function(){
23750         Roo.form.Checkbox.superclass.initEvents.call(this);
23751         this.el.on("click", this.onClick,  this);
23752         this.el.on("change", this.onClick,  this);
23753     },
23754
23755
23756     getResizeEl : function(){
23757         return this.wrap;
23758     },
23759
23760     getPositionEl : function(){
23761         return this.wrap;
23762     },
23763
23764     // private
23765     onRender : function(ct, position){
23766         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
23767         /*
23768         if(this.inputValue !== undefined){
23769             this.el.dom.value = this.inputValue;
23770         }
23771         */
23772         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
23773         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
23774         var viewEl = this.wrap.createChild({ 
23775             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
23776         this.viewEl = viewEl;   
23777         this.wrap.on('click', this.onClick,  this); 
23778         
23779         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
23780         this.el.on('propertychange', this.setFromHidden,  this);  //ie
23781         
23782         
23783         
23784         if(this.boxLabel){
23785             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
23786         //    viewEl.on('click', this.onClick,  this); 
23787         }
23788         //if(this.checked){
23789             this.setChecked(this.checked);
23790         //}else{
23791             //this.checked = this.el.dom;
23792         //}
23793
23794     },
23795
23796     // private
23797     initValue : Roo.emptyFn,
23798
23799     /**
23800      * Returns the checked state of the checkbox.
23801      * @return {Boolean} True if checked, else false
23802      */
23803     getValue : function(){
23804         if(this.el){
23805             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
23806         }
23807         return this.valueOff;
23808         
23809     },
23810
23811         // private
23812     onClick : function(){ 
23813         this.setChecked(!this.checked);
23814
23815         //if(this.el.dom.checked != this.checked){
23816         //    this.setValue(this.el.dom.checked);
23817        // }
23818     },
23819
23820     /**
23821      * Sets the checked state of the checkbox.
23822      * On is always based on a string comparison between inputValue and the param.
23823      * @param {Boolean/String} value - the value to set 
23824      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
23825      */
23826     setValue : function(v,suppressEvent){
23827         
23828         
23829         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
23830         //if(this.el && this.el.dom){
23831         //    this.el.dom.checked = this.checked;
23832         //    this.el.dom.defaultChecked = this.checked;
23833         //}
23834         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
23835         //this.fireEvent("check", this, this.checked);
23836     },
23837     // private..
23838     setChecked : function(state,suppressEvent)
23839     {
23840         if (this.inSetChecked) {
23841             this.checked = state;
23842             return;
23843         }
23844         
23845     
23846         if(this.wrap){
23847             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
23848         }
23849         this.checked = state;
23850         if(suppressEvent !== true){
23851             this.fireEvent('check', this, state);
23852         }
23853         this.inSetChecked = true;
23854         this.el.dom.value = state ? this.inputValue : this.valueOff;
23855         this.inSetChecked = false;
23856         
23857     },
23858     // handle setting of hidden value by some other method!!?!?
23859     setFromHidden: function()
23860     {
23861         if(!this.el){
23862             return;
23863         }
23864         //console.log("SET FROM HIDDEN");
23865         //alert('setFrom hidden');
23866         this.setValue(this.el.dom.value);
23867     },
23868     
23869     onDestroy : function()
23870     {
23871         if(this.viewEl){
23872             Roo.get(this.viewEl).remove();
23873         }
23874          
23875         Roo.form.Checkbox.superclass.onDestroy.call(this);
23876     }
23877
23878 });/*
23879  * Based on:
23880  * Ext JS Library 1.1.1
23881  * Copyright(c) 2006-2007, Ext JS, LLC.
23882  *
23883  * Originally Released Under LGPL - original licence link has changed is not relivant.
23884  *
23885  * Fork - LGPL
23886  * <script type="text/javascript">
23887  */
23888  
23889 /**
23890  * @class Roo.form.Radio
23891  * @extends Roo.form.Checkbox
23892  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
23893  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
23894  * @constructor
23895  * Creates a new Radio
23896  * @param {Object} config Configuration options
23897  */
23898 Roo.form.Radio = function(){
23899     Roo.form.Radio.superclass.constructor.apply(this, arguments);
23900 };
23901 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
23902     inputType: 'radio',
23903
23904     /**
23905      * If this radio is part of a group, it will return the selected value
23906      * @return {String}
23907      */
23908     getGroupValue : function(){
23909         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
23910     }
23911 });//<script type="text/javascript">
23912
23913 /*
23914  * Ext JS Library 1.1.1
23915  * Copyright(c) 2006-2007, Ext JS, LLC.
23916  * licensing@extjs.com
23917  * 
23918  * http://www.extjs.com/license
23919  */
23920  
23921  /*
23922   * 
23923   * Known bugs:
23924   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
23925   * - IE ? - no idea how much works there.
23926   * 
23927   * 
23928   * 
23929   */
23930  
23931
23932 /**
23933  * @class Ext.form.HtmlEditor
23934  * @extends Ext.form.Field
23935  * Provides a lightweight HTML Editor component.
23936  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
23937  * 
23938  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
23939  * supported by this editor.</b><br/><br/>
23940  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
23941  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23942  */
23943 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
23944       /**
23945      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23946      */
23947     toolbars : false,
23948     /**
23949      * @cfg {String} createLinkText The default text for the create link prompt
23950      */
23951     createLinkText : 'Please enter the URL for the link:',
23952     /**
23953      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
23954      */
23955     defaultLinkValue : 'http:/'+'/',
23956    
23957     
23958     // id of frame..
23959     frameId: false,
23960     
23961     // private properties
23962     validationEvent : false,
23963     deferHeight: true,
23964     initialized : false,
23965     activated : false,
23966     sourceEditMode : false,
23967     onFocus : Roo.emptyFn,
23968     iframePad:3,
23969     hideMode:'offsets',
23970     defaultAutoCreate : {
23971         tag: "textarea",
23972         style:"width:500px;height:300px;",
23973         autocomplete: "off"
23974     },
23975
23976     // private
23977     initComponent : function(){
23978         this.addEvents({
23979             /**
23980              * @event initialize
23981              * Fires when the editor is fully initialized (including the iframe)
23982              * @param {HtmlEditor} this
23983              */
23984             initialize: true,
23985             /**
23986              * @event activate
23987              * Fires when the editor is first receives the focus. Any insertion must wait
23988              * until after this event.
23989              * @param {HtmlEditor} this
23990              */
23991             activate: true,
23992              /**
23993              * @event beforesync
23994              * Fires before the textarea is updated with content from the editor iframe. Return false
23995              * to cancel the sync.
23996              * @param {HtmlEditor} this
23997              * @param {String} html
23998              */
23999             beforesync: true,
24000              /**
24001              * @event beforepush
24002              * Fires before the iframe editor is updated with content from the textarea. Return false
24003              * to cancel the push.
24004              * @param {HtmlEditor} this
24005              * @param {String} html
24006              */
24007             beforepush: true,
24008              /**
24009              * @event sync
24010              * Fires when the textarea is updated with content from the editor iframe.
24011              * @param {HtmlEditor} this
24012              * @param {String} html
24013              */
24014             sync: true,
24015              /**
24016              * @event push
24017              * Fires when the iframe editor is updated with content from the textarea.
24018              * @param {HtmlEditor} this
24019              * @param {String} html
24020              */
24021             push: true,
24022              /**
24023              * @event editmodechange
24024              * Fires when the editor switches edit modes
24025              * @param {HtmlEditor} this
24026              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
24027              */
24028             editmodechange: true,
24029             /**
24030              * @event editorevent
24031              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24032              * @param {HtmlEditor} this
24033              */
24034             editorevent: true
24035         })
24036     },
24037
24038     /**
24039      * Protected method that will not generally be called directly. It
24040      * is called when the editor creates its toolbar. Override this method if you need to
24041      * add custom toolbar buttons.
24042      * @param {HtmlEditor} editor
24043      */
24044     createToolbar : function(editor){
24045         if (!editor.toolbars || !editor.toolbars.length) {
24046             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
24047         }
24048         
24049         for (var i =0 ; i < editor.toolbars.length;i++) {
24050             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
24051             editor.toolbars[i].init(editor);
24052         }
24053          
24054         
24055     },
24056
24057     /**
24058      * Protected method that will not generally be called directly. It
24059      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24060      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24061      */
24062     getDocMarkup : function(){
24063         return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
24064     },
24065
24066     // private
24067     onRender : function(ct, position){
24068         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
24069         this.el.dom.style.border = '0 none';
24070         this.el.dom.setAttribute('tabIndex', -1);
24071         this.el.addClass('x-hidden');
24072         if(Roo.isIE){ // fix IE 1px bogus margin
24073             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24074         }
24075         this.wrap = this.el.wrap({
24076             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
24077         });
24078
24079         this.frameId = Roo.id();
24080         this.createToolbar(this);
24081         
24082         
24083         
24084         
24085       
24086         
24087         var iframe = this.wrap.createChild({
24088             tag: 'iframe',
24089             id: this.frameId,
24090             name: this.frameId,
24091             frameBorder : 'no',
24092             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24093         });
24094         
24095        // console.log(iframe);
24096         //this.wrap.dom.appendChild(iframe);
24097
24098         this.iframe = iframe.dom;
24099
24100          this.assignDocWin();
24101         
24102         this.doc.designMode = 'on';
24103        
24104         this.doc.open();
24105         this.doc.write(this.getDocMarkup());
24106         this.doc.close();
24107
24108         
24109         var task = { // must defer to wait for browser to be ready
24110             run : function(){
24111                 //console.log("run task?" + this.doc.readyState);
24112                 this.assignDocWin();
24113                 if(this.doc.body || this.doc.readyState == 'complete'){
24114                     try {
24115                         this.doc.designMode="on";
24116                     } catch (e) {
24117                         return;
24118                     }
24119                     Roo.TaskMgr.stop(task);
24120                     this.initEditor.defer(10, this);
24121                 }
24122             },
24123             interval : 10,
24124             duration:10000,
24125             scope: this
24126         };
24127         Roo.TaskMgr.start(task);
24128
24129         if(!this.width){
24130             this.setSize(this.el.getSize());
24131         }
24132     },
24133
24134     // private
24135     onResize : function(w, h){
24136         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
24137         if(this.el && this.iframe){
24138             if(typeof w == 'number'){
24139                 var aw = w - this.wrap.getFrameWidth('lr');
24140                 this.el.setWidth(this.adjustWidth('textarea', aw));
24141                 this.iframe.style.width = aw + 'px';
24142             }
24143             if(typeof h == 'number'){
24144                 var tbh = 0;
24145                 for (var i =0; i < this.toolbars.length;i++) {
24146                     // fixme - ask toolbars for heights?
24147                     tbh += this.toolbars[i].tb.el.getHeight();
24148                 }
24149                 
24150                 
24151                 
24152                 
24153                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24154                 this.el.setHeight(this.adjustWidth('textarea', ah));
24155                 this.iframe.style.height = ah + 'px';
24156                 if(this.doc){
24157                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
24158                 }
24159             }
24160         }
24161     },
24162
24163     /**
24164      * Toggles the editor between standard and source edit mode.
24165      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24166      */
24167     toggleSourceEdit : function(sourceEditMode){
24168         
24169         this.sourceEditMode = sourceEditMode === true;
24170         
24171         if(this.sourceEditMode){
24172           
24173             this.syncValue();
24174             this.iframe.className = 'x-hidden';
24175             this.el.removeClass('x-hidden');
24176             this.el.dom.removeAttribute('tabIndex');
24177             this.el.focus();
24178         }else{
24179              
24180             this.pushValue();
24181             this.iframe.className = '';
24182             this.el.addClass('x-hidden');
24183             this.el.dom.setAttribute('tabIndex', -1);
24184             this.deferFocus();
24185         }
24186         this.setSize(this.wrap.getSize());
24187         this.fireEvent('editmodechange', this, this.sourceEditMode);
24188     },
24189
24190     // private used internally
24191     createLink : function(){
24192         var url = prompt(this.createLinkText, this.defaultLinkValue);
24193         if(url && url != 'http:/'+'/'){
24194             this.relayCmd('createlink', url);
24195         }
24196     },
24197
24198     // private (for BoxComponent)
24199     adjustSize : Roo.BoxComponent.prototype.adjustSize,
24200
24201     // private (for BoxComponent)
24202     getResizeEl : function(){
24203         return this.wrap;
24204     },
24205
24206     // private (for BoxComponent)
24207     getPositionEl : function(){
24208         return this.wrap;
24209     },
24210
24211     // private
24212     initEvents : function(){
24213         this.originalValue = this.getValue();
24214     },
24215
24216     /**
24217      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24218      * @method
24219      */
24220     markInvalid : Roo.emptyFn,
24221     /**
24222      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24223      * @method
24224      */
24225     clearInvalid : Roo.emptyFn,
24226
24227     setValue : function(v){
24228         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
24229         this.pushValue();
24230     },
24231
24232     /**
24233      * Protected method that will not generally be called directly. If you need/want
24234      * custom HTML cleanup, this is the method you should override.
24235      * @param {String} html The HTML to be cleaned
24236      * return {String} The cleaned HTML
24237      */
24238     cleanHtml : function(html){
24239         html = String(html);
24240         if(html.length > 5){
24241             if(Roo.isSafari){ // strip safari nonsense
24242                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24243             }
24244         }
24245         if(html == '&nbsp;'){
24246             html = '';
24247         }
24248         return html;
24249     },
24250
24251     /**
24252      * Protected method that will not generally be called directly. Syncs the contents
24253      * of the editor iframe with the textarea.
24254      */
24255     syncValue : function(){
24256         if(this.initialized){
24257             var bd = (this.doc.body || this.doc.documentElement);
24258             this.cleanUpPaste();
24259             var html = bd.innerHTML;
24260             if(Roo.isSafari){
24261                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24262                 var m = bs.match(/text-align:(.*?);/i);
24263                 if(m && m[1]){
24264                     html = '<div style="'+m[0]+'">' + html + '</div>';
24265                 }
24266             }
24267             html = this.cleanHtml(html);
24268             if(this.fireEvent('beforesync', this, html) !== false){
24269                 this.el.dom.value = html;
24270                 this.fireEvent('sync', this, html);
24271             }
24272         }
24273     },
24274
24275     /**
24276      * Protected method that will not generally be called directly. Pushes the value of the textarea
24277      * into the iframe editor.
24278      */
24279     pushValue : function(){
24280         if(this.initialized){
24281             var v = this.el.dom.value;
24282             if(v.length < 1){
24283                 v = '&#160;';
24284             }
24285             
24286             if(this.fireEvent('beforepush', this, v) !== false){
24287                 var d = (this.doc.body || this.doc.documentElement);
24288                 d.innerHTML = v;
24289                 this.cleanUpPaste();
24290                 this.el.dom.value = d.innerHTML;
24291                 this.fireEvent('push', this, v);
24292             }
24293         }
24294     },
24295
24296     // private
24297     deferFocus : function(){
24298         this.focus.defer(10, this);
24299     },
24300
24301     // doc'ed in Field
24302     focus : function(){
24303         if(this.win && !this.sourceEditMode){
24304             this.win.focus();
24305         }else{
24306             this.el.focus();
24307         }
24308     },
24309     
24310     assignDocWin: function()
24311     {
24312         var iframe = this.iframe;
24313         
24314          if(Roo.isIE){
24315             this.doc = iframe.contentWindow.document;
24316             this.win = iframe.contentWindow;
24317         } else {
24318             if (!Roo.get(this.frameId)) {
24319                 return;
24320             }
24321             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24322             this.win = Roo.get(this.frameId).dom.contentWindow;
24323         }
24324     },
24325     
24326     // private
24327     initEditor : function(){
24328         //console.log("INIT EDITOR");
24329         this.assignDocWin();
24330         
24331         
24332         
24333         this.doc.designMode="on";
24334         this.doc.open();
24335         this.doc.write(this.getDocMarkup());
24336         this.doc.close();
24337         
24338         var dbody = (this.doc.body || this.doc.documentElement);
24339         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24340         // this copies styles from the containing element into thsi one..
24341         // not sure why we need all of this..
24342         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24343         ss['background-attachment'] = 'fixed'; // w3c
24344         dbody.bgProperties = 'fixed'; // ie
24345         Roo.DomHelper.applyStyles(dbody, ss);
24346         Roo.EventManager.on(this.doc, {
24347             'mousedown': this.onEditorEvent,
24348             'dblclick': this.onEditorEvent,
24349             'click': this.onEditorEvent,
24350             'keyup': this.onEditorEvent,
24351             buffer:100,
24352             scope: this
24353         });
24354         if(Roo.isGecko){
24355             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24356         }
24357         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24358             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24359         }
24360         this.initialized = true;
24361
24362         this.fireEvent('initialize', this);
24363         this.pushValue();
24364     },
24365
24366     // private
24367     onDestroy : function(){
24368         
24369         
24370         
24371         if(this.rendered){
24372             
24373             for (var i =0; i < this.toolbars.length;i++) {
24374                 // fixme - ask toolbars for heights?
24375                 this.toolbars[i].onDestroy();
24376             }
24377             
24378             this.wrap.dom.innerHTML = '';
24379             this.wrap.remove();
24380         }
24381     },
24382
24383     // private
24384     onFirstFocus : function(){
24385         
24386         this.assignDocWin();
24387         
24388         
24389         this.activated = true;
24390         for (var i =0; i < this.toolbars.length;i++) {
24391             this.toolbars[i].onFirstFocus();
24392         }
24393        
24394         if(Roo.isGecko){ // prevent silly gecko errors
24395             this.win.focus();
24396             var s = this.win.getSelection();
24397             if(!s.focusNode || s.focusNode.nodeType != 3){
24398                 var r = s.getRangeAt(0);
24399                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24400                 r.collapse(true);
24401                 this.deferFocus();
24402             }
24403             try{
24404                 this.execCmd('useCSS', true);
24405                 this.execCmd('styleWithCSS', false);
24406             }catch(e){}
24407         }
24408         this.fireEvent('activate', this);
24409     },
24410
24411     // private
24412     adjustFont: function(btn){
24413         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24414         //if(Roo.isSafari){ // safari
24415         //    adjust *= 2;
24416        // }
24417         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24418         if(Roo.isSafari){ // safari
24419             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24420             v =  (v < 10) ? 10 : v;
24421             v =  (v > 48) ? 48 : v;
24422             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24423             
24424         }
24425         
24426         
24427         v = Math.max(1, v+adjust);
24428         
24429         this.execCmd('FontSize', v  );
24430     },
24431
24432     onEditorEvent : function(e){
24433         this.fireEvent('editorevent', this, e);
24434       //  this.updateToolbar();
24435         this.syncValue();
24436     },
24437
24438     insertTag : function(tg)
24439     {
24440         // could be a bit smarter... -> wrap the current selected tRoo..
24441         
24442         this.execCmd("formatblock",   tg);
24443         
24444     },
24445     
24446     insertText : function(txt)
24447     {
24448         
24449         
24450         range = this.createRange();
24451         range.deleteContents();
24452                //alert(Sender.getAttribute('label'));
24453                
24454         range.insertNode(this.doc.createTextNode(txt));
24455     } ,
24456     
24457     // private
24458     relayBtnCmd : function(btn){
24459         this.relayCmd(btn.cmd);
24460     },
24461
24462     /**
24463      * Executes a Midas editor command on the editor document and performs necessary focus and
24464      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24465      * @param {String} cmd The Midas command
24466      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24467      */
24468     relayCmd : function(cmd, value){
24469         this.win.focus();
24470         this.execCmd(cmd, value);
24471         this.fireEvent('editorevent', this);
24472         //this.updateToolbar();
24473         this.deferFocus();
24474     },
24475
24476     /**
24477      * Executes a Midas editor command directly on the editor document.
24478      * For visual commands, you should use {@link #relayCmd} instead.
24479      * <b>This should only be called after the editor is initialized.</b>
24480      * @param {String} cmd The Midas command
24481      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24482      */
24483     execCmd : function(cmd, value){
24484         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24485         this.syncValue();
24486     },
24487
24488    
24489     /**
24490      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24491      * to insert tRoo.
24492      * @param {String} text
24493      */
24494     insertAtCursor : function(text){
24495         if(!this.activated){
24496             return;
24497         }
24498         if(Roo.isIE){
24499             this.win.focus();
24500             var r = this.doc.selection.createRange();
24501             if(r){
24502                 r.collapse(true);
24503                 r.pasteHTML(text);
24504                 this.syncValue();
24505                 this.deferFocus();
24506             }
24507         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24508             this.win.focus();
24509             this.execCmd('InsertHTML', text);
24510             this.deferFocus();
24511         }
24512     },
24513  // private
24514     mozKeyPress : function(e){
24515         if(e.ctrlKey){
24516             var c = e.getCharCode(), cmd;
24517           
24518             if(c > 0){
24519                 c = String.fromCharCode(c).toLowerCase();
24520                 switch(c){
24521                     case 'b':
24522                         cmd = 'bold';
24523                     break;
24524                     case 'i':
24525                         cmd = 'italic';
24526                     break;
24527                     case 'u':
24528                         cmd = 'underline';
24529                     case 'v':
24530                         this.cleanUpPaste.defer(100, this);
24531                         return;
24532                     break;
24533                 }
24534                 if(cmd){
24535                     this.win.focus();
24536                     this.execCmd(cmd);
24537                     this.deferFocus();
24538                     e.preventDefault();
24539                 }
24540                 
24541             }
24542         }
24543     },
24544
24545     // private
24546     fixKeys : function(){ // load time branching for fastest keydown performance
24547         if(Roo.isIE){
24548             return function(e){
24549                 var k = e.getKey(), r;
24550                 if(k == e.TAB){
24551                     e.stopEvent();
24552                     r = this.doc.selection.createRange();
24553                     if(r){
24554                         r.collapse(true);
24555                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24556                         this.deferFocus();
24557                     }
24558                     return;
24559                 }
24560                 
24561                 if(k == e.ENTER){
24562                     r = this.doc.selection.createRange();
24563                     if(r){
24564                         var target = r.parentElement();
24565                         if(!target || target.tagName.toLowerCase() != 'li'){
24566                             e.stopEvent();
24567                             r.pasteHTML('<br />');
24568                             r.collapse(false);
24569                             r.select();
24570                         }
24571                     }
24572                 }
24573                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24574                     this.cleanUpPaste.defer(100, this);
24575                     return;
24576                 }
24577                 
24578                 
24579             };
24580         }else if(Roo.isOpera){
24581             return function(e){
24582                 var k = e.getKey();
24583                 if(k == e.TAB){
24584                     e.stopEvent();
24585                     this.win.focus();
24586                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24587                     this.deferFocus();
24588                 }
24589                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24590                     this.cleanUpPaste.defer(100, this);
24591                     return;
24592                 }
24593                 
24594             };
24595         }else if(Roo.isSafari){
24596             return function(e){
24597                 var k = e.getKey();
24598                 
24599                 if(k == e.TAB){
24600                     e.stopEvent();
24601                     this.execCmd('InsertText','\t');
24602                     this.deferFocus();
24603                     return;
24604                 }
24605                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24606                     this.cleanUpPaste.defer(100, this);
24607                     return;
24608                 }
24609                 
24610              };
24611         }
24612     }(),
24613     
24614     getAllAncestors: function()
24615     {
24616         var p = this.getSelectedNode();
24617         var a = [];
24618         if (!p) {
24619             a.push(p); // push blank onto stack..
24620             p = this.getParentElement();
24621         }
24622         
24623         
24624         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24625             a.push(p);
24626             p = p.parentNode;
24627         }
24628         a.push(this.doc.body);
24629         return a;
24630     },
24631     lastSel : false,
24632     lastSelNode : false,
24633     
24634     
24635     getSelection : function() 
24636     {
24637         this.assignDocWin();
24638         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24639     },
24640     
24641     getSelectedNode: function() 
24642     {
24643         // this may only work on Gecko!!!
24644         
24645         // should we cache this!!!!
24646         
24647         
24648         
24649          
24650         var range = this.createRange(this.getSelection());
24651         
24652         if (Roo.isIE) {
24653             var parent = range.parentElement();
24654             while (true) {
24655                 var testRange = range.duplicate();
24656                 testRange.moveToElementText(parent);
24657                 if (testRange.inRange(range)) {
24658                     break;
24659                 }
24660                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24661                     break;
24662                 }
24663                 parent = parent.parentElement;
24664             }
24665             return parent;
24666         }
24667         
24668         
24669         var ar = range.endContainer.childNodes;
24670         if (!ar.length) {
24671             ar = range.commonAncestorContainer.childNodes;
24672             //alert(ar.length);
24673         }
24674         var nodes = [];
24675         var other_nodes = [];
24676         var has_other_nodes = false;
24677         for (var i=0;i<ar.length;i++) {
24678             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24679                 continue;
24680             }
24681             // fullly contained node.
24682             
24683             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24684                 nodes.push(ar[i]);
24685                 continue;
24686             }
24687             
24688             // probably selected..
24689             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24690                 other_nodes.push(ar[i]);
24691                 continue;
24692             }
24693             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24694                 continue;
24695             }
24696             
24697             
24698             has_other_nodes = true;
24699         }
24700         if (!nodes.length && other_nodes.length) {
24701             nodes= other_nodes;
24702         }
24703         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24704             return false;
24705         }
24706         
24707         return nodes[0];
24708     },
24709     createRange: function(sel)
24710     {
24711         // this has strange effects when using with 
24712         // top toolbar - not sure if it's a great idea.
24713         //this.editor.contentWindow.focus();
24714         if (typeof sel != "undefined") {
24715             try {
24716                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24717             } catch(e) {
24718                 return this.doc.createRange();
24719             }
24720         } else {
24721             return this.doc.createRange();
24722         }
24723     },
24724     getParentElement: function()
24725     {
24726         
24727         this.assignDocWin();
24728         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24729         
24730         var range = this.createRange(sel);
24731          
24732         try {
24733             var p = range.commonAncestorContainer;
24734             while (p.nodeType == 3) { // text node
24735                 p = p.parentNode;
24736             }
24737             return p;
24738         } catch (e) {
24739             return null;
24740         }
24741     
24742     },
24743     
24744     
24745     
24746     // BC Hacks - cause I cant work out what i was trying to do..
24747     rangeIntersectsNode : function(range, node)
24748     {
24749         var nodeRange = node.ownerDocument.createRange();
24750         try {
24751             nodeRange.selectNode(node);
24752         }
24753         catch (e) {
24754             nodeRange.selectNodeContents(node);
24755         }
24756
24757         return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
24758                  range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
24759     },
24760     rangeCompareNode : function(range, node) {
24761         var nodeRange = node.ownerDocument.createRange();
24762         try {
24763             nodeRange.selectNode(node);
24764         } catch (e) {
24765             nodeRange.selectNodeContents(node);
24766         }
24767         var nodeIsBefore = range.compareBoundaryPoints(Range.START_TO_START, nodeRange) == 1;
24768         var nodeIsAfter = range.compareBoundaryPoints(Range.END_TO_END, nodeRange) == -1;
24769
24770         if (nodeIsBefore && !nodeIsAfter)
24771             return 0;
24772         if (!nodeIsBefore && nodeIsAfter)
24773             return 1;
24774         if (nodeIsBefore && nodeIsAfter)
24775             return 2;
24776
24777         return 3;
24778     },
24779
24780     // private? - in a new class?
24781     cleanUpPaste :  function()
24782     {
24783         // cleans up the whole document..
24784       //  console.log('cleanuppaste');
24785         this.cleanUpChildren(this.doc.body);
24786         
24787         
24788     },
24789     cleanUpChildren : function (n)
24790     {
24791         if (!n.childNodes.length) {
24792             return;
24793         }
24794         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24795            this.cleanUpChild(n.childNodes[i]);
24796         }
24797     },
24798     
24799     
24800         
24801     
24802     cleanUpChild : function (node)
24803     {
24804         //console.log(node);
24805         if (node.nodeName == "#text") {
24806             // clean up silly Windows -- stuff?
24807             return; 
24808         }
24809         if (node.nodeName == "#comment") {
24810             node.parentNode.removeChild(node);
24811             // clean up silly Windows -- stuff?
24812             return; 
24813         }
24814         
24815         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
24816             // remove node.
24817             node.parentNode.removeChild(node);
24818             return;
24819             
24820         }
24821         if (!node.attributes || !node.attributes.length) {
24822             this.cleanUpChildren(node);
24823             return;
24824         }
24825         
24826         function cleanAttr(n,v)
24827         {
24828             
24829             if (v.match(/^\./) || v.match(/^\//)) {
24830                 return;
24831             }
24832             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
24833                 return;
24834             }
24835             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
24836             node.removeAttribute(n);
24837             
24838         }
24839         
24840         function cleanStyle(n,v)
24841         {
24842             if (v.match(/expression/)) { //XSS?? should we even bother..
24843                 node.removeAttribute(n);
24844                 return;
24845             }
24846             
24847             
24848             var parts = v.split(/;/);
24849             Roo.each(parts, function(p) {
24850                 p = p.replace(/\s+/g,'');
24851                 if (!p.length) {
24852                     return;
24853                 }
24854                 var l = p.split(':').shift().replace(/\s+/g,'');
24855                 
24856                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
24857                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
24858                     node.removeAttribute(n);
24859                     return false;
24860                 }
24861             });
24862             
24863             
24864         }
24865         
24866         
24867         for (var i = node.attributes.length-1; i > -1 ; i--) {
24868             var a = node.attributes[i];
24869             //console.log(a);
24870             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
24871                 node.removeAttribute(a.name);
24872                 return;
24873             }
24874             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
24875                 cleanAttr(a.name,a.value); // fixme..
24876                 return;
24877             }
24878             if (a.name == 'style') {
24879                 cleanStyle(a.name,a.value);
24880             }
24881             /// clean up MS crap..
24882             if (a.name == 'class') {
24883                 if (a.value.match(/^Mso/)) {
24884                     node.className = '';
24885                 }
24886             }
24887             
24888             // style cleanup!?
24889             // class cleanup?
24890             
24891         }
24892         
24893         
24894         this.cleanUpChildren(node);
24895         
24896         
24897     }
24898     
24899     
24900     // hide stuff that is not compatible
24901     /**
24902      * @event blur
24903      * @hide
24904      */
24905     /**
24906      * @event change
24907      * @hide
24908      */
24909     /**
24910      * @event focus
24911      * @hide
24912      */
24913     /**
24914      * @event specialkey
24915      * @hide
24916      */
24917     /**
24918      * @cfg {String} fieldClass @hide
24919      */
24920     /**
24921      * @cfg {String} focusClass @hide
24922      */
24923     /**
24924      * @cfg {String} autoCreate @hide
24925      */
24926     /**
24927      * @cfg {String} inputType @hide
24928      */
24929     /**
24930      * @cfg {String} invalidClass @hide
24931      */
24932     /**
24933      * @cfg {String} invalidText @hide
24934      */
24935     /**
24936      * @cfg {String} msgFx @hide
24937      */
24938     /**
24939      * @cfg {String} validateOnBlur @hide
24940      */
24941 });
24942
24943 Roo.form.HtmlEditor.white = [
24944         'area', 'br', 'img', 'input', 'hr', 'wbr',
24945         
24946        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
24947        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
24948        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
24949        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
24950        'table',   'ul',         'xmp', 
24951        
24952        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
24953       'thead',   'tr', 
24954      
24955       'dir', 'menu', 'ol', 'ul', 'dl',
24956        
24957       'embed',  'object'
24958 ];
24959
24960
24961 Roo.form.HtmlEditor.black = [
24962     //    'embed',  'object', // enable - backend responsiblity to clean thiese
24963         'applet', // 
24964         'base',   'basefont', 'bgsound', 'blink',  'body', 
24965         'frame',  'frameset', 'head',    'html',   'ilayer', 
24966         'iframe', 'layer',  'link',     'meta',    'object',   
24967         'script', 'style' ,'title',  'xml' // clean later..
24968 ];
24969 Roo.form.HtmlEditor.clean = [
24970     'script', 'style', 'title', 'xml'
24971 ];
24972
24973 // attributes..
24974
24975 Roo.form.HtmlEditor.ablack = [
24976     'on'
24977 ];
24978     
24979 Roo.form.HtmlEditor.aclean = [ 
24980     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
24981 ];
24982
24983 // protocols..
24984 Roo.form.HtmlEditor.pwhite= [
24985         'http',  'https',  'mailto'
24986 ];
24987
24988 Roo.form.HtmlEditor.cwhite= [
24989         'text-align',
24990         'font-size'
24991 ];
24992
24993 // <script type="text/javascript">
24994 /*
24995  * Based on
24996  * Ext JS Library 1.1.1
24997  * Copyright(c) 2006-2007, Ext JS, LLC.
24998  *  
24999  
25000  */
25001
25002 /**
25003  * @class Roo.form.HtmlEditorToolbar1
25004  * Basic Toolbar
25005  * 
25006  * Usage:
25007  *
25008  new Roo.form.HtmlEditor({
25009     ....
25010     toolbars : [
25011         new Roo.form.HtmlEditorToolbar1({
25012             disable : { fonts: 1 , format: 1, ..., ... , ...],
25013             btns : [ .... ]
25014         })
25015     }
25016      
25017  * 
25018  * @cfg {Object} disable List of elements to disable..
25019  * @cfg {Array} btns List of additional buttons.
25020  * 
25021  * 
25022  * NEEDS Extra CSS? 
25023  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25024  */
25025  
25026 Roo.form.HtmlEditor.ToolbarStandard = function(config)
25027 {
25028     
25029     Roo.apply(this, config);
25030     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25031     // dont call parent... till later.
25032 }
25033
25034 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
25035     
25036     tb: false,
25037     
25038     rendered: false,
25039     
25040     editor : false,
25041     /**
25042      * @cfg {Object} disable  List of toolbar elements to disable
25043          
25044      */
25045     disable : false,
25046       /**
25047      * @cfg {Array} fontFamilies An array of available font families
25048      */
25049     fontFamilies : [
25050         'Arial',
25051         'Courier New',
25052         'Tahoma',
25053         'Times New Roman',
25054         'Verdana'
25055     ],
25056     
25057     specialChars : [
25058            "&#169;",
25059           "&#174;",     
25060           "&#8482;",    
25061           "&#163;" ,    
25062          // "&#8212;",    
25063           "&#8230;",    
25064           "&#247;" ,    
25065         //  "&#225;" ,     ?? a acute?
25066            "&#8364;"    , //Euro
25067        //   "&#8220;"    ,
25068         //  "&#8221;"    ,
25069         //  "&#8226;"    ,
25070           "&#176;"  //   , // degrees
25071
25072          // "&#233;"     , // e ecute
25073          // "&#250;"     , // u ecute?
25074     ],
25075     inputElements : [ 
25076             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
25077             "input:submit", "input:button", "select", "textarea", "label" ],
25078     formats : [
25079         ["p"] ,  
25080         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
25081         ["pre"],[ "code"], 
25082         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
25083     ],
25084      /**
25085      * @cfg {String} defaultFont default font to use.
25086      */
25087     defaultFont: 'tahoma',
25088    
25089     fontSelect : false,
25090     
25091     
25092     formatCombo : false,
25093     
25094     init : function(editor)
25095     {
25096         this.editor = editor;
25097         
25098         
25099         var fid = editor.frameId;
25100         var etb = this;
25101         function btn(id, toggle, handler){
25102             var xid = fid + '-'+ id ;
25103             return {
25104                 id : xid,
25105                 cmd : id,
25106                 cls : 'x-btn-icon x-edit-'+id,
25107                 enableToggle:toggle !== false,
25108                 scope: editor, // was editor...
25109                 handler:handler||editor.relayBtnCmd,
25110                 clickEvent:'mousedown',
25111                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
25112                 tabIndex:-1
25113             };
25114         }
25115         
25116         
25117         
25118         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
25119         this.tb = tb;
25120          // stop form submits
25121         tb.el.on('click', function(e){
25122             e.preventDefault(); // what does this do?
25123         });
25124
25125         if(!this.disable.font && !Roo.isSafari){
25126             /* why no safari for fonts
25127             editor.fontSelect = tb.el.createChild({
25128                 tag:'select',
25129                 tabIndex: -1,
25130                 cls:'x-font-select',
25131                 html: editor.createFontOptions()
25132             });
25133             editor.fontSelect.on('change', function(){
25134                 var font = editor.fontSelect.dom.value;
25135                 editor.relayCmd('fontname', font);
25136                 editor.deferFocus();
25137             }, editor);
25138             tb.add(
25139                 editor.fontSelect.dom,
25140                 '-'
25141             );
25142             */
25143         };
25144         if(!this.disable.formats){
25145             this.formatCombo = new Roo.form.ComboBox({
25146                 store: new Roo.data.SimpleStore({
25147                     id : 'tag',
25148                     fields: ['tag'],
25149                     data : this.formats // from states.js
25150                 }),
25151                 blockFocus : true,
25152                 //autoCreate : {tag: "div",  size: "20"},
25153                 displayField:'tag',
25154                 typeAhead: false,
25155                 mode: 'local',
25156                 editable : false,
25157                 triggerAction: 'all',
25158                 emptyText:'Add tag',
25159                 selectOnFocus:true,
25160                 width:135,
25161                 listeners : {
25162                     'select': function(c, r, i) {
25163                         editor.insertTag(r.get('tag'));
25164                         editor.focus();
25165                     }
25166                 }
25167
25168             });
25169             tb.addField(this.formatCombo);
25170             
25171         }
25172         
25173         if(!this.disable.format){
25174             tb.add(
25175                 btn('bold'),
25176                 btn('italic'),
25177                 btn('underline')
25178             );
25179         };
25180         if(!this.disable.fontSize){
25181             tb.add(
25182                 '-',
25183                 
25184                 
25185                 btn('increasefontsize', false, editor.adjustFont),
25186                 btn('decreasefontsize', false, editor.adjustFont)
25187             );
25188         };
25189         
25190         
25191         if(this.disable.colors){
25192             tb.add(
25193                 '-', {
25194                     id:editor.frameId +'-forecolor',
25195                     cls:'x-btn-icon x-edit-forecolor',
25196                     clickEvent:'mousedown',
25197                     tooltip: this.buttonTips['forecolor'] || undefined,
25198                     tabIndex:-1,
25199                     menu : new Roo.menu.ColorMenu({
25200                         allowReselect: true,
25201                         focus: Roo.emptyFn,
25202                         value:'000000',
25203                         plain:true,
25204                         selectHandler: function(cp, color){
25205                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
25206                             editor.deferFocus();
25207                         },
25208                         scope: editor,
25209                         clickEvent:'mousedown'
25210                     })
25211                 }, {
25212                     id:editor.frameId +'backcolor',
25213                     cls:'x-btn-icon x-edit-backcolor',
25214                     clickEvent:'mousedown',
25215                     tooltip: this.buttonTips['backcolor'] || undefined,
25216                     tabIndex:-1,
25217                     menu : new Roo.menu.ColorMenu({
25218                         focus: Roo.emptyFn,
25219                         value:'FFFFFF',
25220                         plain:true,
25221                         allowReselect: true,
25222                         selectHandler: function(cp, color){
25223                             if(Roo.isGecko){
25224                                 editor.execCmd('useCSS', false);
25225                                 editor.execCmd('hilitecolor', color);
25226                                 editor.execCmd('useCSS', true);
25227                                 editor.deferFocus();
25228                             }else{
25229                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
25230                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
25231                                 editor.deferFocus();
25232                             }
25233                         },
25234                         scope:editor,
25235                         clickEvent:'mousedown'
25236                     })
25237                 }
25238             );
25239         };
25240         // now add all the items...
25241         
25242
25243         if(!this.disable.alignments){
25244             tb.add(
25245                 '-',
25246                 btn('justifyleft'),
25247                 btn('justifycenter'),
25248                 btn('justifyright')
25249             );
25250         };
25251
25252         //if(!Roo.isSafari){
25253             if(!this.disable.links){
25254                 tb.add(
25255                     '-',
25256                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
25257                 );
25258             };
25259
25260             if(!this.disable.lists){
25261                 tb.add(
25262                     '-',
25263                     btn('insertorderedlist'),
25264                     btn('insertunorderedlist')
25265                 );
25266             }
25267             if(!this.disable.sourceEdit){
25268                 tb.add(
25269                     '-',
25270                     btn('sourceedit', true, function(btn){
25271                         this.toggleSourceEdit(btn.pressed);
25272                     })
25273                 );
25274             }
25275         //}
25276         
25277         var smenu = { };
25278         // special menu.. - needs to be tidied up..
25279         if (!this.disable.special) {
25280             smenu = {
25281                 text: "&#169;",
25282                 cls: 'x-edit-none',
25283                 menu : {
25284                     items : []
25285                    }
25286             };
25287             for (var i =0; i < this.specialChars.length; i++) {
25288                 smenu.menu.items.push({
25289                     
25290                     html: this.specialChars[i],
25291                     handler: function(a,b) {
25292                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
25293                         
25294                     },
25295                     tabIndex:-1
25296                 });
25297             }
25298             
25299             
25300             tb.add(smenu);
25301             
25302             
25303         }
25304         if (this.btns) {
25305             for(var i =0; i< this.btns.length;i++) {
25306                 var b = this.btns[i];
25307                 b.cls =  'x-edit-none';
25308                 b.scope = editor;
25309                 tb.add(b);
25310             }
25311         
25312         }
25313         
25314         
25315         
25316         // disable everything...
25317         
25318         this.tb.items.each(function(item){
25319            if(item.id != editor.frameId+ '-sourceedit'){
25320                 item.disable();
25321             }
25322         });
25323         this.rendered = true;
25324         
25325         // the all the btns;
25326         editor.on('editorevent', this.updateToolbar, this);
25327         // other toolbars need to implement this..
25328         //editor.on('editmodechange', this.updateToolbar, this);
25329     },
25330     
25331     
25332     
25333     /**
25334      * Protected method that will not generally be called directly. It triggers
25335      * a toolbar update by reading the markup state of the current selection in the editor.
25336      */
25337     updateToolbar: function(){
25338
25339         if(!this.editor.activated){
25340             this.editor.onFirstFocus();
25341             return;
25342         }
25343
25344         var btns = this.tb.items.map, 
25345             doc = this.editor.doc,
25346             frameId = this.editor.frameId;
25347
25348         if(!this.disable.font && !Roo.isSafari){
25349             /*
25350             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
25351             if(name != this.fontSelect.dom.value){
25352                 this.fontSelect.dom.value = name;
25353             }
25354             */
25355         }
25356         if(!this.disable.format){
25357             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
25358             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
25359             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
25360         }
25361         if(!this.disable.alignments){
25362             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
25363             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
25364             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
25365         }
25366         if(!Roo.isSafari && !this.disable.lists){
25367             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
25368             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
25369         }
25370         
25371         var ans = this.editor.getAllAncestors();
25372         if (this.formatCombo) {
25373             
25374             
25375             var store = this.formatCombo.store;
25376             this.formatCombo.setValue("");
25377             for (var i =0; i < ans.length;i++) {
25378                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
25379                     // select it..
25380                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
25381                     break;
25382                 }
25383             }
25384         }
25385         
25386         
25387         
25388         // hides menus... - so this cant be on a menu...
25389         Roo.menu.MenuMgr.hideAll();
25390
25391         //this.editorsyncValue();
25392     },
25393    
25394     
25395     createFontOptions : function(){
25396         var buf = [], fs = this.fontFamilies, ff, lc;
25397         for(var i = 0, len = fs.length; i< len; i++){
25398             ff = fs[i];
25399             lc = ff.toLowerCase();
25400             buf.push(
25401                 '<option value="',lc,'" style="font-family:',ff,';"',
25402                     (this.defaultFont == lc ? ' selected="true">' : '>'),
25403                     ff,
25404                 '</option>'
25405             );
25406         }
25407         return buf.join('');
25408     },
25409     
25410     toggleSourceEdit : function(sourceEditMode){
25411         if(sourceEditMode === undefined){
25412             sourceEditMode = !this.sourceEditMode;
25413         }
25414         this.sourceEditMode = sourceEditMode === true;
25415         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
25416         // just toggle the button?
25417         if(btn.pressed !== this.editor.sourceEditMode){
25418             btn.toggle(this.editor.sourceEditMode);
25419             return;
25420         }
25421         
25422         if(this.sourceEditMode){
25423             this.tb.items.each(function(item){
25424                 if(item.cmd != 'sourceedit'){
25425                     item.disable();
25426                 }
25427             });
25428           
25429         }else{
25430             if(this.initialized){
25431                 this.tb.items.each(function(item){
25432                     item.enable();
25433                 });
25434             }
25435             
25436         }
25437         // tell the editor that it's been pressed..
25438         this.editor.toggleSourceEdit(sourceEditMode);
25439        
25440     },
25441      /**
25442      * Object collection of toolbar tooltips for the buttons in the editor. The key
25443      * is the command id associated with that button and the value is a valid QuickTips object.
25444      * For example:
25445 <pre><code>
25446 {
25447     bold : {
25448         title: 'Bold (Ctrl+B)',
25449         text: 'Make the selected text bold.',
25450         cls: 'x-html-editor-tip'
25451     },
25452     italic : {
25453         title: 'Italic (Ctrl+I)',
25454         text: 'Make the selected text italic.',
25455         cls: 'x-html-editor-tip'
25456     },
25457     ...
25458 </code></pre>
25459     * @type Object
25460      */
25461     buttonTips : {
25462         bold : {
25463             title: 'Bold (Ctrl+B)',
25464             text: 'Make the selected text bold.',
25465             cls: 'x-html-editor-tip'
25466         },
25467         italic : {
25468             title: 'Italic (Ctrl+I)',
25469             text: 'Make the selected text italic.',
25470             cls: 'x-html-editor-tip'
25471         },
25472         underline : {
25473             title: 'Underline (Ctrl+U)',
25474             text: 'Underline the selected text.',
25475             cls: 'x-html-editor-tip'
25476         },
25477         increasefontsize : {
25478             title: 'Grow Text',
25479             text: 'Increase the font size.',
25480             cls: 'x-html-editor-tip'
25481         },
25482         decreasefontsize : {
25483             title: 'Shrink Text',
25484             text: 'Decrease the font size.',
25485             cls: 'x-html-editor-tip'
25486         },
25487         backcolor : {
25488             title: 'Text Highlight Color',
25489             text: 'Change the background color of the selected text.',
25490             cls: 'x-html-editor-tip'
25491         },
25492         forecolor : {
25493             title: 'Font Color',
25494             text: 'Change the color of the selected text.',
25495             cls: 'x-html-editor-tip'
25496         },
25497         justifyleft : {
25498             title: 'Align Text Left',
25499             text: 'Align text to the left.',
25500             cls: 'x-html-editor-tip'
25501         },
25502         justifycenter : {
25503             title: 'Center Text',
25504             text: 'Center text in the editor.',
25505             cls: 'x-html-editor-tip'
25506         },
25507         justifyright : {
25508             title: 'Align Text Right',
25509             text: 'Align text to the right.',
25510             cls: 'x-html-editor-tip'
25511         },
25512         insertunorderedlist : {
25513             title: 'Bullet List',
25514             text: 'Start a bulleted list.',
25515             cls: 'x-html-editor-tip'
25516         },
25517         insertorderedlist : {
25518             title: 'Numbered List',
25519             text: 'Start a numbered list.',
25520             cls: 'x-html-editor-tip'
25521         },
25522         createlink : {
25523             title: 'Hyperlink',
25524             text: 'Make the selected text a hyperlink.',
25525             cls: 'x-html-editor-tip'
25526         },
25527         sourceedit : {
25528             title: 'Source Edit',
25529             text: 'Switch to source editing mode.',
25530             cls: 'x-html-editor-tip'
25531         }
25532     },
25533     // private
25534     onDestroy : function(){
25535         if(this.rendered){
25536             
25537             this.tb.items.each(function(item){
25538                 if(item.menu){
25539                     item.menu.removeAll();
25540                     if(item.menu.el){
25541                         item.menu.el.destroy();
25542                     }
25543                 }
25544                 item.destroy();
25545             });
25546              
25547         }
25548     },
25549     onFirstFocus: function() {
25550         this.tb.items.each(function(item){
25551            item.enable();
25552         });
25553     }
25554 });
25555
25556
25557
25558
25559 // <script type="text/javascript">
25560 /*
25561  * Based on
25562  * Ext JS Library 1.1.1
25563  * Copyright(c) 2006-2007, Ext JS, LLC.
25564  *  
25565  
25566  */
25567
25568  
25569 /**
25570  * @class Roo.form.HtmlEditor.ToolbarContext
25571  * Context Toolbar
25572  * 
25573  * Usage:
25574  *
25575  new Roo.form.HtmlEditor({
25576     ....
25577     toolbars : [
25578         new Roo.form.HtmlEditor.ToolbarStandard(),
25579         new Roo.form.HtmlEditor.ToolbarContext()
25580         })
25581     }
25582      
25583  * 
25584  * @config : {Object} disable List of elements to disable.. (not done yet.)
25585  * 
25586  * 
25587  */
25588
25589 Roo.form.HtmlEditor.ToolbarContext = function(config)
25590 {
25591     
25592     Roo.apply(this, config);
25593     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25594     // dont call parent... till later.
25595 }
25596 Roo.form.HtmlEditor.ToolbarContext.types = {
25597     'IMG' : {
25598         width : {
25599             title: "Width",
25600             width: 40
25601         },
25602         height:  {
25603             title: "Height",
25604             width: 40
25605         },
25606         align: {
25607             title: "Align",
25608             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
25609             width : 80
25610             
25611         },
25612         border: {
25613             title: "Border",
25614             width: 40
25615         },
25616         alt: {
25617             title: "Alt",
25618             width: 120
25619         },
25620         src : {
25621             title: "Src",
25622             width: 220
25623         }
25624         
25625     },
25626     'A' : {
25627         name : {
25628             title: "Name",
25629             width: 50
25630         },
25631         href:  {
25632             title: "Href",
25633             width: 220
25634         } // border?
25635         
25636     },
25637     'TABLE' : {
25638         rows : {
25639             title: "Rows",
25640             width: 20
25641         },
25642         cols : {
25643             title: "Cols",
25644             width: 20
25645         },
25646         width : {
25647             title: "Width",
25648             width: 40
25649         },
25650         height : {
25651             title: "Height",
25652             width: 40
25653         },
25654         border : {
25655             title: "Border",
25656             width: 20
25657         }
25658     },
25659     'TD' : {
25660         width : {
25661             title: "Width",
25662             width: 40
25663         },
25664         height : {
25665             title: "Height",
25666             width: 40
25667         },   
25668         align: {
25669             title: "Align",
25670             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
25671             width: 40
25672         },
25673         valign: {
25674             title: "Valign",
25675             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
25676             width: 40
25677         },
25678         colspan: {
25679             title: "Colspan",
25680             width: 20
25681             
25682         }
25683     },
25684     'INPUT' : {
25685         name : {
25686             title: "name",
25687             width: 120
25688         },
25689         value : {
25690             title: "Value",
25691             width: 120
25692         },
25693         width : {
25694             title: "Width",
25695             width: 40
25696         }
25697     },
25698     'LABEL' : {
25699         'for' : {
25700             title: "For",
25701             width: 120
25702         }
25703     },
25704     'TEXTAREA' : {
25705           name : {
25706             title: "name",
25707             width: 120
25708         },
25709         rows : {
25710             title: "Rows",
25711             width: 20
25712         },
25713         cols : {
25714             title: "Cols",
25715             width: 20
25716         }
25717     },
25718     'SELECT' : {
25719         name : {
25720             title: "name",
25721             width: 120
25722         },
25723         selectoptions : {
25724             title: "Options",
25725             width: 200
25726         }
25727     },
25728     'BODY' : {
25729         title : {
25730             title: "title",
25731             width: 120,
25732             disabled : true
25733         }
25734     }
25735 };
25736
25737
25738
25739 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
25740     
25741     tb: false,
25742     
25743     rendered: false,
25744     
25745     editor : false,
25746     /**
25747      * @cfg {Object} disable  List of toolbar elements to disable
25748          
25749      */
25750     disable : false,
25751     
25752     
25753     
25754     toolbars : false,
25755     
25756     init : function(editor)
25757     {
25758         this.editor = editor;
25759         
25760         
25761         var fid = editor.frameId;
25762         var etb = this;
25763         function btn(id, toggle, handler){
25764             var xid = fid + '-'+ id ;
25765             return {
25766                 id : xid,
25767                 cmd : id,
25768                 cls : 'x-btn-icon x-edit-'+id,
25769                 enableToggle:toggle !== false,
25770                 scope: editor, // was editor...
25771                 handler:handler||editor.relayBtnCmd,
25772                 clickEvent:'mousedown',
25773                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
25774                 tabIndex:-1
25775             };
25776         }
25777         // create a new element.
25778         var wdiv = editor.wrap.createChild({
25779                 tag: 'div'
25780             }, editor.wrap.dom.firstChild.nextSibling, true);
25781         
25782         // can we do this more than once??
25783         
25784          // stop form submits
25785       
25786  
25787         // disable everything...
25788         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
25789         this.toolbars = {};
25790            
25791         for (var i in  ty) {
25792           
25793             this.toolbars[i] = this.buildToolbar(ty[i],i);
25794         }
25795         this.tb = this.toolbars.BODY;
25796         this.tb.el.show();
25797         
25798          
25799         this.rendered = true;
25800         
25801         // the all the btns;
25802         editor.on('editorevent', this.updateToolbar, this);
25803         // other toolbars need to implement this..
25804         //editor.on('editmodechange', this.updateToolbar, this);
25805     },
25806     
25807     
25808     
25809     /**
25810      * Protected method that will not generally be called directly. It triggers
25811      * a toolbar update by reading the markup state of the current selection in the editor.
25812      */
25813     updateToolbar: function(){
25814
25815         if(!this.editor.activated){
25816             this.editor.onFirstFocus();
25817             return;
25818         }
25819
25820         
25821         var ans = this.editor.getAllAncestors();
25822         
25823         // pick
25824         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
25825         var sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
25826         sel = sel ? sel : this.editor.doc.body;
25827         sel = sel.tagName.length ? sel : this.editor.doc.body;
25828         var tn = sel.tagName.toUpperCase();
25829         sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
25830         tn = sel.tagName.toUpperCase();
25831         if (this.tb.name  == tn) {
25832             return; // no change
25833         }
25834         this.tb.el.hide();
25835         ///console.log("show: " + tn);
25836         this.tb =  this.toolbars[tn];
25837         this.tb.el.show();
25838         this.tb.fields.each(function(e) {
25839             e.setValue(sel.getAttribute(e.name));
25840         });
25841         this.tb.selectedNode = sel;
25842         
25843         
25844         Roo.menu.MenuMgr.hideAll();
25845
25846         //this.editorsyncValue();
25847     },
25848    
25849        
25850     // private
25851     onDestroy : function(){
25852         if(this.rendered){
25853             
25854             this.tb.items.each(function(item){
25855                 if(item.menu){
25856                     item.menu.removeAll();
25857                     if(item.menu.el){
25858                         item.menu.el.destroy();
25859                     }
25860                 }
25861                 item.destroy();
25862             });
25863              
25864         }
25865     },
25866     onFirstFocus: function() {
25867         // need to do this for all the toolbars..
25868         this.tb.items.each(function(item){
25869            item.enable();
25870         });
25871     },
25872     buildToolbar: function(tlist, nm)
25873     {
25874         var editor = this.editor;
25875          // create a new element.
25876         var wdiv = editor.wrap.createChild({
25877                 tag: 'div'
25878             }, editor.wrap.dom.firstChild.nextSibling, true);
25879         
25880        
25881         var tb = new Roo.Toolbar(wdiv);
25882         tb.add(nm+ ":&nbsp;");
25883         for (var i in tlist) {
25884             var item = tlist[i];
25885             tb.add(item.title + ":&nbsp;");
25886             if (item.opts) {
25887                 // fixme
25888                 
25889               
25890                 tb.addField( new Roo.form.ComboBox({
25891                     store: new Roo.data.SimpleStore({
25892                         id : 'val',
25893                         fields: ['val'],
25894                         data : item.opts // from states.js
25895                     }),
25896                     name : i,
25897                     displayField:'val',
25898                     typeAhead: false,
25899                     mode: 'local',
25900                     editable : false,
25901                     triggerAction: 'all',
25902                     emptyText:'Select',
25903                     selectOnFocus:true,
25904                     width: item.width ? item.width  : 130,
25905                     listeners : {
25906                         'select': function(c, r, i) {
25907                             tb.selectedNode.setAttribute(c.name, r.get('val'));
25908                         }
25909                     }
25910
25911                 }));
25912                 continue;
25913                     
25914                 
25915                 
25916                 
25917                 
25918                 tb.addField( new Roo.form.TextField({
25919                     name: i,
25920                     width: 100,
25921                     //allowBlank:false,
25922                     value: ''
25923                 }));
25924                 continue;
25925             }
25926             tb.addField( new Roo.form.TextField({
25927                 name: i,
25928                 width: item.width,
25929                 //allowBlank:true,
25930                 value: '',
25931                 listeners: {
25932                     'change' : function(f, nv, ov) {
25933                         tb.selectedNode.setAttribute(f.name, nv);
25934                     }
25935                 }
25936             }));
25937              
25938         }
25939         tb.el.on('click', function(e){
25940             e.preventDefault(); // what does this do?
25941         });
25942         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
25943         tb.el.hide();
25944         tb.name = nm;
25945         // dont need to disable them... as they will get hidden
25946         return tb;
25947          
25948         
25949     }
25950     
25951     
25952     
25953     
25954 });
25955
25956
25957
25958
25959
25960 /*
25961  * Based on:
25962  * Ext JS Library 1.1.1
25963  * Copyright(c) 2006-2007, Ext JS, LLC.
25964  *
25965  * Originally Released Under LGPL - original licence link has changed is not relivant.
25966  *
25967  * Fork - LGPL
25968  * <script type="text/javascript">
25969  */
25970  
25971 /**
25972  * @class Roo.form.BasicForm
25973  * @extends Roo.util.Observable
25974  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
25975  * @constructor
25976  * @param {String/HTMLElement/Roo.Element} el The form element or its id
25977  * @param {Object} config Configuration options
25978  */
25979 Roo.form.BasicForm = function(el, config){
25980     this.allItems = [];
25981     this.childForms = [];
25982     Roo.apply(this, config);
25983     /*
25984      * The Roo.form.Field items in this form.
25985      * @type MixedCollection
25986      */
25987      
25988      
25989     this.items = new Roo.util.MixedCollection(false, function(o){
25990         return o.id || (o.id = Roo.id());
25991     });
25992     this.addEvents({
25993         /**
25994          * @event beforeaction
25995          * Fires before any action is performed. Return false to cancel the action.
25996          * @param {Form} this
25997          * @param {Action} action The action to be performed
25998          */
25999         beforeaction: true,
26000         /**
26001          * @event actionfailed
26002          * Fires when an action fails.
26003          * @param {Form} this
26004          * @param {Action} action The action that failed
26005          */
26006         actionfailed : true,
26007         /**
26008          * @event actioncomplete
26009          * Fires when an action is completed.
26010          * @param {Form} this
26011          * @param {Action} action The action that completed
26012          */
26013         actioncomplete : true
26014     });
26015     if(el){
26016         this.initEl(el);
26017     }
26018     Roo.form.BasicForm.superclass.constructor.call(this);
26019 };
26020
26021 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
26022     /**
26023      * @cfg {String} method
26024      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
26025      */
26026     /**
26027      * @cfg {DataReader} reader
26028      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
26029      * This is optional as there is built-in support for processing JSON.
26030      */
26031     /**
26032      * @cfg {DataReader} errorReader
26033      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
26034      * This is completely optional as there is built-in support for processing JSON.
26035      */
26036     /**
26037      * @cfg {String} url
26038      * The URL to use for form actions if one isn't supplied in the action options.
26039      */
26040     /**
26041      * @cfg {Boolean} fileUpload
26042      * Set to true if this form is a file upload.
26043      */
26044      
26045     /**
26046      * @cfg {Object} baseParams
26047      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
26048      */
26049      /**
26050      
26051     /**
26052      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
26053      */
26054     timeout: 30,
26055
26056     // private
26057     activeAction : null,
26058
26059     /**
26060      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
26061      * or setValues() data instead of when the form was first created.
26062      */
26063     trackResetOnLoad : false,
26064     
26065     
26066     /**
26067      * childForms - used for multi-tab forms
26068      * @type {Array}
26069      */
26070     childForms : false,
26071     
26072     /**
26073      * allItems - full list of fields.
26074      * @type {Array}
26075      */
26076     allItems : false,
26077     
26078     /**
26079      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
26080      * element by passing it or its id or mask the form itself by passing in true.
26081      * @type Mixed
26082      */
26083     waitMsgTarget : false,
26084
26085     // private
26086     initEl : function(el){
26087         this.el = Roo.get(el);
26088         this.id = this.el.id || Roo.id();
26089         this.el.on('submit', this.onSubmit, this);
26090         this.el.addClass('x-form');
26091     },
26092
26093     // private
26094     onSubmit : function(e){
26095         e.stopEvent();
26096     },
26097
26098     /**
26099      * Returns true if client-side validation on the form is successful.
26100      * @return Boolean
26101      */
26102     isValid : function(){
26103         var valid = true;
26104         this.items.each(function(f){
26105            if(!f.validate()){
26106                valid = false;
26107            }
26108         });
26109         return valid;
26110     },
26111
26112     /**
26113      * Returns true if any fields in this form have changed since their original load.
26114      * @return Boolean
26115      */
26116     isDirty : function(){
26117         var dirty = false;
26118         this.items.each(function(f){
26119            if(f.isDirty()){
26120                dirty = true;
26121                return false;
26122            }
26123         });
26124         return dirty;
26125     },
26126
26127     /**
26128      * Performs a predefined action (submit or load) or custom actions you define on this form.
26129      * @param {String} actionName The name of the action type
26130      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
26131      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
26132      * accept other config options):
26133      * <pre>
26134 Property          Type             Description
26135 ----------------  ---------------  ----------------------------------------------------------------------------------
26136 url               String           The url for the action (defaults to the form's url)
26137 method            String           The form method to use (defaults to the form's method, or POST if not defined)
26138 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
26139 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
26140                                    validate the form on the client (defaults to false)
26141      * </pre>
26142      * @return {BasicForm} this
26143      */
26144     doAction : function(action, options){
26145         if(typeof action == 'string'){
26146             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
26147         }
26148         if(this.fireEvent('beforeaction', this, action) !== false){
26149             this.beforeAction(action);
26150             action.run.defer(100, action);
26151         }
26152         return this;
26153     },
26154
26155     /**
26156      * Shortcut to do a submit action.
26157      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26158      * @return {BasicForm} this
26159      */
26160     submit : function(options){
26161         this.doAction('submit', options);
26162         return this;
26163     },
26164
26165     /**
26166      * Shortcut to do a load action.
26167      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26168      * @return {BasicForm} this
26169      */
26170     load : function(options){
26171         this.doAction('load', options);
26172         return this;
26173     },
26174
26175     /**
26176      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
26177      * @param {Record} record The record to edit
26178      * @return {BasicForm} this
26179      */
26180     updateRecord : function(record){
26181         record.beginEdit();
26182         var fs = record.fields;
26183         fs.each(function(f){
26184             var field = this.findField(f.name);
26185             if(field){
26186                 record.set(f.name, field.getValue());
26187             }
26188         }, this);
26189         record.endEdit();
26190         return this;
26191     },
26192
26193     /**
26194      * Loads an Roo.data.Record into this form.
26195      * @param {Record} record The record to load
26196      * @return {BasicForm} this
26197      */
26198     loadRecord : function(record){
26199         this.setValues(record.data);
26200         return this;
26201     },
26202
26203     // private
26204     beforeAction : function(action){
26205         var o = action.options;
26206         
26207        
26208         if(this.waitMsgTarget === true){
26209             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
26210         }else if(this.waitMsgTarget){
26211             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
26212             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
26213         }else {
26214             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
26215         }
26216          
26217     },
26218
26219     // private
26220     afterAction : function(action, success){
26221         this.activeAction = null;
26222         var o = action.options;
26223         
26224         if(this.waitMsgTarget === true){
26225             this.el.unmask();
26226         }else if(this.waitMsgTarget){
26227             this.waitMsgTarget.unmask();
26228         }else{
26229             Roo.MessageBox.updateProgress(1);
26230             Roo.MessageBox.hide();
26231         }
26232          
26233         if(success){
26234             if(o.reset){
26235                 this.reset();
26236             }
26237             Roo.callback(o.success, o.scope, [this, action]);
26238             this.fireEvent('actioncomplete', this, action);
26239             
26240         }else{
26241             Roo.callback(o.failure, o.scope, [this, action]);
26242             // show an error message if no failed handler is set..
26243             if (!this.hasListener('actionfailed')) {
26244                 Roo.MessageBox.alert("Error", "Saving Failed, please check your entries");
26245             }
26246             
26247             this.fireEvent('actionfailed', this, action);
26248         }
26249         
26250     },
26251
26252     /**
26253      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
26254      * @param {String} id The value to search for
26255      * @return Field
26256      */
26257     findField : function(id){
26258         var field = this.items.get(id);
26259         if(!field){
26260             this.items.each(function(f){
26261                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
26262                     field = f;
26263                     return false;
26264                 }
26265             });
26266         }
26267         return field || null;
26268     },
26269
26270     /**
26271      * Add a secondary form to this one, 
26272      * Used to provide tabbed forms. One form is primary, with hidden values 
26273      * which mirror the elements from the other forms.
26274      * 
26275      * @param {Roo.form.Form} form to add.
26276      * 
26277      */
26278     addForm : function(form)
26279     {
26280        
26281         if (this.childForms.indexOf(form) > -1) {
26282             // already added..
26283             return;
26284         }
26285         this.childForms.push(form);
26286         var n = '';
26287         Roo.each(form.allItems, function (fe) {
26288             
26289             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
26290             if (this.findField(n)) { // already added..
26291                 return;
26292             }
26293             var add = new Roo.form.Hidden({
26294                 name : n
26295             });
26296             add.render(this.el);
26297             
26298             this.add( add );
26299         }, this);
26300         
26301     },
26302     /**
26303      * Mark fields in this form invalid in bulk.
26304      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
26305      * @return {BasicForm} this
26306      */
26307     markInvalid : function(errors){
26308         if(errors instanceof Array){
26309             for(var i = 0, len = errors.length; i < len; i++){
26310                 var fieldError = errors[i];
26311                 var f = this.findField(fieldError.id);
26312                 if(f){
26313                     f.markInvalid(fieldError.msg);
26314                 }
26315             }
26316         }else{
26317             var field, id;
26318             for(id in errors){
26319                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
26320                     field.markInvalid(errors[id]);
26321                 }
26322             }
26323         }
26324         Roo.each(this.childForms || [], function (f) {
26325             f.markInvalid(errors);
26326         });
26327         
26328         return this;
26329     },
26330
26331     /**
26332      * Set values for fields in this form in bulk.
26333      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
26334      * @return {BasicForm} this
26335      */
26336     setValues : function(values){
26337         if(values instanceof Array){ // array of objects
26338             for(var i = 0, len = values.length; i < len; i++){
26339                 var v = values[i];
26340                 var f = this.findField(v.id);
26341                 if(f){
26342                     f.setValue(v.value);
26343                     if(this.trackResetOnLoad){
26344                         f.originalValue = f.getValue();
26345                     }
26346                 }
26347             }
26348         }else{ // object hash
26349             var field, id;
26350             for(id in values){
26351                 if(typeof values[id] != 'function' && (field = this.findField(id))){
26352                     
26353                     if (field.setFromData && 
26354                         field.valueField && 
26355                         field.displayField &&
26356                         // combos' with local stores can 
26357                         // be queried via setValue()
26358                         // to set their value..
26359                         (field.store && !field.store.isLocal)
26360                         ) {
26361                         // it's a combo
26362                         var sd = { };
26363                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
26364                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
26365                         field.setFromData(sd);
26366                         
26367                     } else {
26368                         field.setValue(values[id]);
26369                     }
26370                     
26371                     
26372                     if(this.trackResetOnLoad){
26373                         field.originalValue = field.getValue();
26374                     }
26375                 }
26376             }
26377         }
26378          
26379         Roo.each(this.childForms || [], function (f) {
26380             f.setValues(values);
26381         });
26382                 
26383         return this;
26384     },
26385
26386     /**
26387      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
26388      * they are returned as an array.
26389      * @param {Boolean} asString
26390      * @return {Object}
26391      */
26392     getValues : function(asString){
26393         if (this.childForms) {
26394             // copy values from the child forms
26395             Roo.each(this.childForms, function (f) {
26396                 this.setValues(f.getValues());
26397             }, this);
26398         }
26399         
26400         
26401         
26402         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
26403         if(asString === true){
26404             return fs;
26405         }
26406         return Roo.urlDecode(fs);
26407     },
26408     
26409     /**
26410      * Returns the fields in this form as an object with key/value pairs. 
26411      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
26412      * @return {Object}
26413      */
26414     getFieldValues : function()
26415     {
26416         if (this.childForms) {
26417             // copy values from the child forms
26418             Roo.each(this.childForms, function (f) {
26419                 this.setValues(f.getValues());
26420             }, this);
26421         }
26422         
26423         var ret = {};
26424         this.items.each(function(f){
26425             if (!f.getName()) {
26426                 return;
26427             }
26428             var v = f.getValue();
26429             if ((typeof(v) == 'object') && f.getRawValue) {
26430                 v = f.getRawValue() ; // dates..
26431             }
26432             ret[f.getName()] = v;
26433         });
26434         
26435         return ret;
26436     },
26437
26438     /**
26439      * Clears all invalid messages in this form.
26440      * @return {BasicForm} this
26441      */
26442     clearInvalid : function(){
26443         this.items.each(function(f){
26444            f.clearInvalid();
26445         });
26446         
26447         Roo.each(this.childForms || [], function (f) {
26448             f.clearInvalid();
26449         });
26450         
26451         
26452         return this;
26453     },
26454
26455     /**
26456      * Resets this form.
26457      * @return {BasicForm} this
26458      */
26459     reset : function(){
26460         this.items.each(function(f){
26461             f.reset();
26462         });
26463         
26464         Roo.each(this.childForms || [], function (f) {
26465             f.reset();
26466         });
26467        
26468         
26469         return this;
26470     },
26471
26472     /**
26473      * Add Roo.form components to this form.
26474      * @param {Field} field1
26475      * @param {Field} field2 (optional)
26476      * @param {Field} etc (optional)
26477      * @return {BasicForm} this
26478      */
26479     add : function(){
26480         this.items.addAll(Array.prototype.slice.call(arguments, 0));
26481         return this;
26482     },
26483
26484
26485     /**
26486      * Removes a field from the items collection (does NOT remove its markup).
26487      * @param {Field} field
26488      * @return {BasicForm} this
26489      */
26490     remove : function(field){
26491         this.items.remove(field);
26492         return this;
26493     },
26494
26495     /**
26496      * Looks at the fields in this form, checks them for an id attribute,
26497      * and calls applyTo on the existing dom element with that id.
26498      * @return {BasicForm} this
26499      */
26500     render : function(){
26501         this.items.each(function(f){
26502             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
26503                 f.applyTo(f.id);
26504             }
26505         });
26506         return this;
26507     },
26508
26509     /**
26510      * Calls {@link Ext#apply} for all fields in this form with the passed object.
26511      * @param {Object} values
26512      * @return {BasicForm} this
26513      */
26514     applyToFields : function(o){
26515         this.items.each(function(f){
26516            Roo.apply(f, o);
26517         });
26518         return this;
26519     },
26520
26521     /**
26522      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
26523      * @param {Object} values
26524      * @return {BasicForm} this
26525      */
26526     applyIfToFields : function(o){
26527         this.items.each(function(f){
26528            Roo.applyIf(f, o);
26529         });
26530         return this;
26531     }
26532 });
26533
26534 // back compat
26535 Roo.BasicForm = Roo.form.BasicForm;/*
26536  * Based on:
26537  * Ext JS Library 1.1.1
26538  * Copyright(c) 2006-2007, Ext JS, LLC.
26539  *
26540  * Originally Released Under LGPL - original licence link has changed is not relivant.
26541  *
26542  * Fork - LGPL
26543  * <script type="text/javascript">
26544  */
26545
26546 /**
26547  * @class Roo.form.Form
26548  * @extends Roo.form.BasicForm
26549  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
26550  * @constructor
26551  * @param {Object} config Configuration options
26552  */
26553 Roo.form.Form = function(config){
26554     var xitems =  [];
26555     if (config.items) {
26556         xitems = config.items;
26557         delete config.items;
26558     }
26559    
26560     
26561     Roo.form.Form.superclass.constructor.call(this, null, config);
26562     this.url = this.url || this.action;
26563     if(!this.root){
26564         this.root = new Roo.form.Layout(Roo.applyIf({
26565             id: Roo.id()
26566         }, config));
26567     }
26568     this.active = this.root;
26569     /**
26570      * Array of all the buttons that have been added to this form via {@link addButton}
26571      * @type Array
26572      */
26573     this.buttons = [];
26574     this.allItems = [];
26575     this.addEvents({
26576         /**
26577          * @event clientvalidation
26578          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
26579          * @param {Form} this
26580          * @param {Boolean} valid true if the form has passed client-side validation
26581          */
26582         clientvalidation: true,
26583         /**
26584          * @event rendered
26585          * Fires when the form is rendered
26586          * @param {Roo.form.Form} form
26587          */
26588         rendered : true
26589     });
26590     
26591     if (this.progressUrl) {
26592             // push a hidden field onto the list of fields..
26593             this.addxtype( {
26594                     xns: Roo.form, 
26595                     xtype : 'Hidden', 
26596                     name : 'UPLOAD_IDENTIFIER' 
26597             });
26598         }
26599         
26600     
26601     Roo.each(xitems, this.addxtype, this);
26602     
26603     
26604     
26605 };
26606
26607 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
26608     /**
26609      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
26610      */
26611     /**
26612      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
26613      */
26614     /**
26615      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
26616      */
26617     buttonAlign:'center',
26618
26619     /**
26620      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
26621      */
26622     minButtonWidth:75,
26623
26624     /**
26625      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
26626      * This property cascades to child containers if not set.
26627      */
26628     labelAlign:'left',
26629
26630     /**
26631      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
26632      * fires a looping event with that state. This is required to bind buttons to the valid
26633      * state using the config value formBind:true on the button.
26634      */
26635     monitorValid : false,
26636
26637     /**
26638      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
26639      */
26640     monitorPoll : 200,
26641     
26642     /**
26643      * @cfg {String} progressUrl - Url to return progress data 
26644      */
26645     
26646     progressUrl : false,
26647   
26648     /**
26649      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
26650      * fields are added and the column is closed. If no fields are passed the column remains open
26651      * until end() is called.
26652      * @param {Object} config The config to pass to the column
26653      * @param {Field} field1 (optional)
26654      * @param {Field} field2 (optional)
26655      * @param {Field} etc (optional)
26656      * @return Column The column container object
26657      */
26658     column : function(c){
26659         var col = new Roo.form.Column(c);
26660         this.start(col);
26661         if(arguments.length > 1){ // duplicate code required because of Opera
26662             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26663             this.end();
26664         }
26665         return col;
26666     },
26667
26668     /**
26669      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
26670      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
26671      * until end() is called.
26672      * @param {Object} config The config to pass to the fieldset
26673      * @param {Field} field1 (optional)
26674      * @param {Field} field2 (optional)
26675      * @param {Field} etc (optional)
26676      * @return FieldSet The fieldset container object
26677      */
26678     fieldset : function(c){
26679         var fs = new Roo.form.FieldSet(c);
26680         this.start(fs);
26681         if(arguments.length > 1){ // duplicate code required because of Opera
26682             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26683             this.end();
26684         }
26685         return fs;
26686     },
26687
26688     /**
26689      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
26690      * fields are added and the container is closed. If no fields are passed the container remains open
26691      * until end() is called.
26692      * @param {Object} config The config to pass to the Layout
26693      * @param {Field} field1 (optional)
26694      * @param {Field} field2 (optional)
26695      * @param {Field} etc (optional)
26696      * @return Layout The container object
26697      */
26698     container : function(c){
26699         var l = new Roo.form.Layout(c);
26700         this.start(l);
26701         if(arguments.length > 1){ // duplicate code required because of Opera
26702             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26703             this.end();
26704         }
26705         return l;
26706     },
26707
26708     /**
26709      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
26710      * @param {Object} container A Roo.form.Layout or subclass of Layout
26711      * @return {Form} this
26712      */
26713     start : function(c){
26714         // cascade label info
26715         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
26716         this.active.stack.push(c);
26717         c.ownerCt = this.active;
26718         this.active = c;
26719         return this;
26720     },
26721
26722     /**
26723      * Closes the current open container
26724      * @return {Form} this
26725      */
26726     end : function(){
26727         if(this.active == this.root){
26728             return this;
26729         }
26730         this.active = this.active.ownerCt;
26731         return this;
26732     },
26733
26734     /**
26735      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
26736      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
26737      * as the label of the field.
26738      * @param {Field} field1
26739      * @param {Field} field2 (optional)
26740      * @param {Field} etc. (optional)
26741      * @return {Form} this
26742      */
26743     add : function(){
26744         this.active.stack.push.apply(this.active.stack, arguments);
26745         this.allItems.push.apply(this.allItems,arguments);
26746         var r = [];
26747         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
26748             if(a[i].isFormField){
26749                 r.push(a[i]);
26750             }
26751         }
26752         if(r.length > 0){
26753             Roo.form.Form.superclass.add.apply(this, r);
26754         }
26755         return this;
26756     },
26757     
26758
26759     
26760     
26761     
26762      /**
26763      * Find any element that has been added to a form, using it's ID or name
26764      * This can include framesets, columns etc. along with regular fields..
26765      * @param {String} id - id or name to find.
26766      
26767      * @return {Element} e - or false if nothing found.
26768      */
26769     findbyId : function(id)
26770     {
26771         var ret = false;
26772         if (!id) {
26773             return ret;
26774         }
26775         Roo.each(this.allItems, function(f){
26776             if (f.id == id || f.name == id ){
26777                 ret = f;
26778                 return false;
26779             }
26780         });
26781         return ret;
26782     },
26783
26784     
26785     
26786     /**
26787      * Render this form into the passed container. This should only be called once!
26788      * @param {String/HTMLElement/Element} container The element this component should be rendered into
26789      * @return {Form} this
26790      */
26791     render : function(ct)
26792     {
26793         
26794         
26795         
26796         ct = Roo.get(ct);
26797         var o = this.autoCreate || {
26798             tag: 'form',
26799             method : this.method || 'POST',
26800             id : this.id || Roo.id()
26801         };
26802         this.initEl(ct.createChild(o));
26803
26804         this.root.render(this.el);
26805         
26806        
26807              
26808         this.items.each(function(f){
26809             f.render('x-form-el-'+f.id);
26810         });
26811
26812         if(this.buttons.length > 0){
26813             // tables are required to maintain order and for correct IE layout
26814             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
26815                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
26816                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
26817             }}, null, true);
26818             var tr = tb.getElementsByTagName('tr')[0];
26819             for(var i = 0, len = this.buttons.length; i < len; i++) {
26820                 var b = this.buttons[i];
26821                 var td = document.createElement('td');
26822                 td.className = 'x-form-btn-td';
26823                 b.render(tr.appendChild(td));
26824             }
26825         }
26826         if(this.monitorValid){ // initialize after render
26827             this.startMonitoring();
26828         }
26829         this.fireEvent('rendered', this);
26830         return this;
26831     },
26832
26833     /**
26834      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
26835      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
26836      * object or a valid Roo.DomHelper element config
26837      * @param {Function} handler The function called when the button is clicked
26838      * @param {Object} scope (optional) The scope of the handler function
26839      * @return {Roo.Button}
26840      */
26841     addButton : function(config, handler, scope){
26842         var bc = {
26843             handler: handler,
26844             scope: scope,
26845             minWidth: this.minButtonWidth,
26846             hideParent:true
26847         };
26848         if(typeof config == "string"){
26849             bc.text = config;
26850         }else{
26851             Roo.apply(bc, config);
26852         }
26853         var btn = new Roo.Button(null, bc);
26854         this.buttons.push(btn);
26855         return btn;
26856     },
26857
26858      /**
26859      * Adds a series of form elements (using the xtype property as the factory method.
26860      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
26861      * @param {Object} config 
26862      */
26863     
26864     addxtype : function()
26865     {
26866         var ar = Array.prototype.slice.call(arguments, 0);
26867         var ret = false;
26868         for(var i = 0; i < ar.length; i++) {
26869             if (!ar[i]) {
26870                 continue; // skip -- if this happends something invalid got sent, we 
26871                 // should ignore it, as basically that interface element will not show up
26872                 // and that should be pretty obvious!!
26873             }
26874             
26875             if (Roo.form[ar[i].xtype]) {
26876                 ar[i].form = this;
26877                 var fe = Roo.factory(ar[i], Roo.form);
26878                 if (!ret) {
26879                     ret = fe;
26880                 }
26881                 fe.form = this;
26882                 if (fe.store) {
26883                     fe.store.form = this;
26884                 }
26885                 if (fe.isLayout) {  
26886                          
26887                     this.start(fe);
26888                     this.allItems.push(fe);
26889                     if (fe.items && fe.addxtype) {
26890                         fe.addxtype.apply(fe, fe.items);
26891                         delete fe.items;
26892                     }
26893                      this.end();
26894                     continue;
26895                 }
26896                 
26897                 
26898                  
26899                 this.add(fe);
26900               //  console.log('adding ' + ar[i].xtype);
26901             }
26902             if (ar[i].xtype == 'Button') {  
26903                 //console.log('adding button');
26904                 //console.log(ar[i]);
26905                 this.addButton(ar[i]);
26906                 this.allItems.push(fe);
26907                 continue;
26908             }
26909             
26910             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
26911                 alert('end is not supported on xtype any more, use items');
26912             //    this.end();
26913             //    //console.log('adding end');
26914             }
26915             
26916         }
26917         return ret;
26918     },
26919     
26920     /**
26921      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
26922      * option "monitorValid"
26923      */
26924     startMonitoring : function(){
26925         if(!this.bound){
26926             this.bound = true;
26927             Roo.TaskMgr.start({
26928                 run : this.bindHandler,
26929                 interval : this.monitorPoll || 200,
26930                 scope: this
26931             });
26932         }
26933     },
26934
26935     /**
26936      * Stops monitoring of the valid state of this form
26937      */
26938     stopMonitoring : function(){
26939         this.bound = false;
26940     },
26941
26942     // private
26943     bindHandler : function(){
26944         if(!this.bound){
26945             return false; // stops binding
26946         }
26947         var valid = true;
26948         this.items.each(function(f){
26949             if(!f.isValid(true)){
26950                 valid = false;
26951                 return false;
26952             }
26953         });
26954         for(var i = 0, len = this.buttons.length; i < len; i++){
26955             var btn = this.buttons[i];
26956             if(btn.formBind === true && btn.disabled === valid){
26957                 btn.setDisabled(!valid);
26958             }
26959         }
26960         this.fireEvent('clientvalidation', this, valid);
26961     }
26962     
26963     
26964     
26965     
26966     
26967     
26968     
26969     
26970 });
26971
26972
26973 // back compat
26974 Roo.Form = Roo.form.Form;
26975 /*
26976  * Based on:
26977  * Ext JS Library 1.1.1
26978  * Copyright(c) 2006-2007, Ext JS, LLC.
26979  *
26980  * Originally Released Under LGPL - original licence link has changed is not relivant.
26981  *
26982  * Fork - LGPL
26983  * <script type="text/javascript">
26984  */
26985  
26986  /**
26987  * @class Roo.form.Action
26988  * Internal Class used to handle form actions
26989  * @constructor
26990  * @param {Roo.form.BasicForm} el The form element or its id
26991  * @param {Object} config Configuration options
26992  */
26993  
26994  
26995 // define the action interface
26996 Roo.form.Action = function(form, options){
26997     this.form = form;
26998     this.options = options || {};
26999 };
27000 /**
27001  * Client Validation Failed
27002  * @const 
27003  */
27004 Roo.form.Action.CLIENT_INVALID = 'client';
27005 /**
27006  * Server Validation Failed
27007  * @const 
27008  */
27009  Roo.form.Action.SERVER_INVALID = 'server';
27010  /**
27011  * Connect to Server Failed
27012  * @const 
27013  */
27014 Roo.form.Action.CONNECT_FAILURE = 'connect';
27015 /**
27016  * Reading Data from Server Failed
27017  * @const 
27018  */
27019 Roo.form.Action.LOAD_FAILURE = 'load';
27020
27021 Roo.form.Action.prototype = {
27022     type : 'default',
27023     failureType : undefined,
27024     response : undefined,
27025     result : undefined,
27026
27027     // interface method
27028     run : function(options){
27029
27030     },
27031
27032     // interface method
27033     success : function(response){
27034
27035     },
27036
27037     // interface method
27038     handleResponse : function(response){
27039
27040     },
27041
27042     // default connection failure
27043     failure : function(response){
27044         
27045         this.response = response;
27046         this.failureType = Roo.form.Action.CONNECT_FAILURE;
27047         this.form.afterAction(this, false);
27048     },
27049
27050     processResponse : function(response){
27051         this.response = response;
27052         if(!response.responseText){
27053             return true;
27054         }
27055         this.result = this.handleResponse(response);
27056         return this.result;
27057     },
27058
27059     // utility functions used internally
27060     getUrl : function(appendParams){
27061         var url = this.options.url || this.form.url || this.form.el.dom.action;
27062         if(appendParams){
27063             var p = this.getParams();
27064             if(p){
27065                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
27066             }
27067         }
27068         return url;
27069     },
27070
27071     getMethod : function(){
27072         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
27073     },
27074
27075     getParams : function(){
27076         var bp = this.form.baseParams;
27077         var p = this.options.params;
27078         if(p){
27079             if(typeof p == "object"){
27080                 p = Roo.urlEncode(Roo.applyIf(p, bp));
27081             }else if(typeof p == 'string' && bp){
27082                 p += '&' + Roo.urlEncode(bp);
27083             }
27084         }else if(bp){
27085             p = Roo.urlEncode(bp);
27086         }
27087         return p;
27088     },
27089
27090     createCallback : function(){
27091         return {
27092             success: this.success,
27093             failure: this.failure,
27094             scope: this,
27095             timeout: (this.form.timeout*1000),
27096             upload: this.form.fileUpload ? this.success : undefined
27097         };
27098     }
27099 };
27100
27101 Roo.form.Action.Submit = function(form, options){
27102     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
27103 };
27104
27105 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
27106     type : 'submit',
27107
27108     haveProgress : false,
27109     uploadComplete : false,
27110     
27111     // uploadProgress indicator.
27112     uploadProgress : function()
27113     {
27114         if (!this.form.progressUrl) {
27115             return;
27116         }
27117         
27118         if (!this.haveProgress) {
27119             Roo.MessageBox.progress("Uploading", "Uploading");
27120         }
27121         if (this.uploadComplete) {
27122            Roo.MessageBox.hide();
27123            return;
27124         }
27125         
27126         this.haveProgress = true;
27127    
27128         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
27129         
27130         var c = new Roo.data.Connection();
27131         c.request({
27132             url : this.form.progressUrl,
27133             params: {
27134                 id : uid
27135             },
27136             method: 'GET',
27137             success : function(req){
27138                //console.log(data);
27139                 var rdata = false;
27140                 var edata;
27141                 try  {
27142                    rdata = Roo.decode(req.responseText)
27143                 } catch (e) {
27144                     Roo.log("Invalid data from server..");
27145                     Roo.log(edata);
27146                     return;
27147                 }
27148                 if (!rdata || !rdata.success) {
27149                     Roo.log(rdata);
27150                     return;
27151                 }
27152                 var data = rdata.data;
27153                 
27154                 if (this.uploadComplete) {
27155                    Roo.MessageBox.hide();
27156                    return;
27157                 }
27158                    
27159                 if (data){
27160                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
27161                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
27162                     );
27163                 }
27164                 this.uploadProgress.defer(2000,this);
27165             },
27166        
27167             failure: function(data) {
27168                 Roo.log('progress url failed ');
27169                 Roo.log(data);
27170             },
27171             scope : this
27172         });
27173            
27174     },
27175     
27176     
27177     run : function()
27178     {
27179         // run get Values on the form, so it syncs any secondary forms.
27180         this.form.getValues();
27181         
27182         var o = this.options;
27183         var method = this.getMethod();
27184         var isPost = method == 'POST';
27185         if(o.clientValidation === false || this.form.isValid()){
27186             
27187             if (this.form.progressUrl) {
27188                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
27189                     (new Date() * 1) + '' + Math.random());
27190                     
27191             } 
27192             
27193             
27194             Roo.Ajax.request(Roo.apply(this.createCallback(), {
27195                 form:this.form.el.dom,
27196                 url:this.getUrl(!isPost),
27197                 method: method,
27198                 params:isPost ? this.getParams() : null,
27199                 isUpload: this.form.fileUpload
27200             }));
27201             
27202             this.uploadProgress();
27203
27204         }else if (o.clientValidation !== false){ // client validation failed
27205             this.failureType = Roo.form.Action.CLIENT_INVALID;
27206             this.form.afterAction(this, false);
27207         }
27208     },
27209
27210     success : function(response)
27211     {
27212         this.uploadComplete= true;
27213         if (this.haveProgress) {
27214             Roo.MessageBox.hide();
27215         }
27216         
27217         
27218         var result = this.processResponse(response);
27219         if(result === true || result.success){
27220             this.form.afterAction(this, true);
27221             return;
27222         }
27223         if(result.errors){
27224             this.form.markInvalid(result.errors);
27225             this.failureType = Roo.form.Action.SERVER_INVALID;
27226         }
27227         this.form.afterAction(this, false);
27228     },
27229     failure : function(response)
27230     {
27231         this.uploadComplete= true;
27232         if (this.haveProgress) {
27233             Roo.MessageBox.hide();
27234         }
27235         
27236         
27237         this.response = response;
27238         this.failureType = Roo.form.Action.CONNECT_FAILURE;
27239         this.form.afterAction(this, false);
27240     },
27241     
27242     handleResponse : function(response){
27243         if(this.form.errorReader){
27244             var rs = this.form.errorReader.read(response);
27245             var errors = [];
27246             if(rs.records){
27247                 for(var i = 0, len = rs.records.length; i < len; i++) {
27248                     var r = rs.records[i];
27249                     errors[i] = r.data;
27250                 }
27251             }
27252             if(errors.length < 1){
27253                 errors = null;
27254             }
27255             return {
27256                 success : rs.success,
27257                 errors : errors
27258             };
27259         }
27260         var ret = false;
27261         try {
27262             ret = Roo.decode(response.responseText);
27263         } catch (e) {
27264             ret = {
27265                 success: false,
27266                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
27267                 errors : []
27268             };
27269         }
27270         return ret;
27271         
27272     }
27273 });
27274
27275
27276 Roo.form.Action.Load = function(form, options){
27277     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
27278     this.reader = this.form.reader;
27279 };
27280
27281 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
27282     type : 'load',
27283
27284     run : function(){
27285         
27286         Roo.Ajax.request(Roo.apply(
27287                 this.createCallback(), {
27288                     method:this.getMethod(),
27289                     url:this.getUrl(false),
27290                     params:this.getParams()
27291         }));
27292     },
27293
27294     success : function(response){
27295         
27296         var result = this.processResponse(response);
27297         if(result === true || !result.success || !result.data){
27298             this.failureType = Roo.form.Action.LOAD_FAILURE;
27299             this.form.afterAction(this, false);
27300             return;
27301         }
27302         this.form.clearInvalid();
27303         this.form.setValues(result.data);
27304         this.form.afterAction(this, true);
27305     },
27306
27307     handleResponse : function(response){
27308         if(this.form.reader){
27309             var rs = this.form.reader.read(response);
27310             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
27311             return {
27312                 success : rs.success,
27313                 data : data
27314             };
27315         }
27316         return Roo.decode(response.responseText);
27317     }
27318 });
27319
27320 Roo.form.Action.ACTION_TYPES = {
27321     'load' : Roo.form.Action.Load,
27322     'submit' : Roo.form.Action.Submit
27323 };/*
27324  * Based on:
27325  * Ext JS Library 1.1.1
27326  * Copyright(c) 2006-2007, Ext JS, LLC.
27327  *
27328  * Originally Released Under LGPL - original licence link has changed is not relivant.
27329  *
27330  * Fork - LGPL
27331  * <script type="text/javascript">
27332  */
27333  
27334 /**
27335  * @class Roo.form.Layout
27336  * @extends Roo.Component
27337  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
27338  * @constructor
27339  * @param {Object} config Configuration options
27340  */
27341 Roo.form.Layout = function(config){
27342     var xitems = [];
27343     if (config.items) {
27344         xitems = config.items;
27345         delete config.items;
27346     }
27347     Roo.form.Layout.superclass.constructor.call(this, config);
27348     this.stack = [];
27349     Roo.each(xitems, this.addxtype, this);
27350      
27351 };
27352
27353 Roo.extend(Roo.form.Layout, Roo.Component, {
27354     /**
27355      * @cfg {String/Object} autoCreate
27356      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
27357      */
27358     /**
27359      * @cfg {String/Object/Function} style
27360      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
27361      * a function which returns such a specification.
27362      */
27363     /**
27364      * @cfg {String} labelAlign
27365      * Valid values are "left," "top" and "right" (defaults to "left")
27366      */
27367     /**
27368      * @cfg {Number} labelWidth
27369      * Fixed width in pixels of all field labels (defaults to undefined)
27370      */
27371     /**
27372      * @cfg {Boolean} clear
27373      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
27374      */
27375     clear : true,
27376     /**
27377      * @cfg {String} labelSeparator
27378      * The separator to use after field labels (defaults to ':')
27379      */
27380     labelSeparator : ':',
27381     /**
27382      * @cfg {Boolean} hideLabels
27383      * True to suppress the display of field labels in this layout (defaults to false)
27384      */
27385     hideLabels : false,
27386
27387     // private
27388     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
27389     
27390     isLayout : true,
27391     
27392     // private
27393     onRender : function(ct, position){
27394         if(this.el){ // from markup
27395             this.el = Roo.get(this.el);
27396         }else {  // generate
27397             var cfg = this.getAutoCreate();
27398             this.el = ct.createChild(cfg, position);
27399         }
27400         if(this.style){
27401             this.el.applyStyles(this.style);
27402         }
27403         if(this.labelAlign){
27404             this.el.addClass('x-form-label-'+this.labelAlign);
27405         }
27406         if(this.hideLabels){
27407             this.labelStyle = "display:none";
27408             this.elementStyle = "padding-left:0;";
27409         }else{
27410             if(typeof this.labelWidth == 'number'){
27411                 this.labelStyle = "width:"+this.labelWidth+"px;";
27412                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
27413             }
27414             if(this.labelAlign == 'top'){
27415                 this.labelStyle = "width:auto;";
27416                 this.elementStyle = "padding-left:0;";
27417             }
27418         }
27419         var stack = this.stack;
27420         var slen = stack.length;
27421         if(slen > 0){
27422             if(!this.fieldTpl){
27423                 var t = new Roo.Template(
27424                     '<div class="x-form-item {5}">',
27425                         '<label for="{0}" style="{2}">{1}{4}</label>',
27426                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
27427                         '</div>',
27428                     '</div><div class="x-form-clear-left"></div>'
27429                 );
27430                 t.disableFormats = true;
27431                 t.compile();
27432                 Roo.form.Layout.prototype.fieldTpl = t;
27433             }
27434             for(var i = 0; i < slen; i++) {
27435                 if(stack[i].isFormField){
27436                     this.renderField(stack[i]);
27437                 }else{
27438                     this.renderComponent(stack[i]);
27439                 }
27440             }
27441         }
27442         if(this.clear){
27443             this.el.createChild({cls:'x-form-clear'});
27444         }
27445     },
27446
27447     // private
27448     renderField : function(f){
27449         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
27450                f.id, //0
27451                f.fieldLabel, //1
27452                f.labelStyle||this.labelStyle||'', //2
27453                this.elementStyle||'', //3
27454                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
27455                f.itemCls||this.itemCls||''  //5
27456        ], true).getPrevSibling());
27457     },
27458
27459     // private
27460     renderComponent : function(c){
27461         c.render(c.isLayout ? this.el : this.el.createChild());    
27462     },
27463     /**
27464      * Adds a object form elements (using the xtype property as the factory method.)
27465      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
27466      * @param {Object} config 
27467      */
27468     addxtype : function(o)
27469     {
27470         // create the lement.
27471         o.form = this.form;
27472         var fe = Roo.factory(o, Roo.form);
27473         this.form.allItems.push(fe);
27474         this.stack.push(fe);
27475         
27476         if (fe.isFormField) {
27477             this.form.items.add(fe);
27478         }
27479          
27480         return fe;
27481     }
27482 });
27483
27484 /**
27485  * @class Roo.form.Column
27486  * @extends Roo.form.Layout
27487  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
27488  * @constructor
27489  * @param {Object} config Configuration options
27490  */
27491 Roo.form.Column = function(config){
27492     Roo.form.Column.superclass.constructor.call(this, config);
27493 };
27494
27495 Roo.extend(Roo.form.Column, Roo.form.Layout, {
27496     /**
27497      * @cfg {Number/String} width
27498      * The fixed width of the column in pixels or CSS value (defaults to "auto")
27499      */
27500     /**
27501      * @cfg {String/Object} autoCreate
27502      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
27503      */
27504
27505     // private
27506     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
27507
27508     // private
27509     onRender : function(ct, position){
27510         Roo.form.Column.superclass.onRender.call(this, ct, position);
27511         if(this.width){
27512             this.el.setWidth(this.width);
27513         }
27514     }
27515 });
27516
27517
27518 /**
27519  * @class Roo.form.Row
27520  * @extends Roo.form.Layout
27521  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
27522  * @constructor
27523  * @param {Object} config Configuration options
27524  */
27525
27526  
27527 Roo.form.Row = function(config){
27528     Roo.form.Row.superclass.constructor.call(this, config);
27529 };
27530  
27531 Roo.extend(Roo.form.Row, Roo.form.Layout, {
27532       /**
27533      * @cfg {Number/String} width
27534      * The fixed width of the column in pixels or CSS value (defaults to "auto")
27535      */
27536     /**
27537      * @cfg {Number/String} height
27538      * The fixed height of the column in pixels or CSS value (defaults to "auto")
27539      */
27540     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
27541     
27542     padWidth : 20,
27543     // private
27544     onRender : function(ct, position){
27545         //console.log('row render');
27546         if(!this.rowTpl){
27547             var t = new Roo.Template(
27548                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
27549                     '<label for="{0}" style="{2}">{1}{4}</label>',
27550                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
27551                     '</div>',
27552                 '</div>'
27553             );
27554             t.disableFormats = true;
27555             t.compile();
27556             Roo.form.Layout.prototype.rowTpl = t;
27557         }
27558         this.fieldTpl = this.rowTpl;
27559         
27560         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
27561         var labelWidth = 100;
27562         
27563         if ((this.labelAlign != 'top')) {
27564             if (typeof this.labelWidth == 'number') {
27565                 labelWidth = this.labelWidth
27566             }
27567             this.padWidth =  20 + labelWidth;
27568             
27569         }
27570         
27571         Roo.form.Column.superclass.onRender.call(this, ct, position);
27572         if(this.width){
27573             this.el.setWidth(this.width);
27574         }
27575         if(this.height){
27576             this.el.setHeight(this.height);
27577         }
27578     },
27579     
27580     // private
27581     renderField : function(f){
27582         f.fieldEl = this.fieldTpl.append(this.el, [
27583                f.id, f.fieldLabel,
27584                f.labelStyle||this.labelStyle||'',
27585                this.elementStyle||'',
27586                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
27587                f.itemCls||this.itemCls||'',
27588                f.width ? f.width + this.padWidth : 160 + this.padWidth
27589        ],true);
27590     }
27591 });
27592  
27593
27594 /**
27595  * @class Roo.form.FieldSet
27596  * @extends Roo.form.Layout
27597  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
27598  * @constructor
27599  * @param {Object} config Configuration options
27600  */
27601 Roo.form.FieldSet = function(config){
27602     Roo.form.FieldSet.superclass.constructor.call(this, config);
27603 };
27604
27605 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
27606     /**
27607      * @cfg {String} legend
27608      * The text to display as the legend for the FieldSet (defaults to '')
27609      */
27610     /**
27611      * @cfg {String/Object} autoCreate
27612      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
27613      */
27614
27615     // private
27616     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
27617
27618     // private
27619     onRender : function(ct, position){
27620         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
27621         if(this.legend){
27622             this.setLegend(this.legend);
27623         }
27624     },
27625
27626     // private
27627     setLegend : function(text){
27628         if(this.rendered){
27629             this.el.child('legend').update(text);
27630         }
27631     }
27632 });/*
27633  * Based on:
27634  * Ext JS Library 1.1.1
27635  * Copyright(c) 2006-2007, Ext JS, LLC.
27636  *
27637  * Originally Released Under LGPL - original licence link has changed is not relivant.
27638  *
27639  * Fork - LGPL
27640  * <script type="text/javascript">
27641  */
27642 /**
27643  * @class Roo.form.VTypes
27644  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
27645  * @singleton
27646  */
27647 Roo.form.VTypes = function(){
27648     // closure these in so they are only created once.
27649     var alpha = /^[a-zA-Z_]+$/;
27650     var alphanum = /^[a-zA-Z0-9_]+$/;
27651     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
27652     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
27653
27654     // All these messages and functions are configurable
27655     return {
27656         /**
27657          * The function used to validate email addresses
27658          * @param {String} value The email address
27659          */
27660         'email' : function(v){
27661             return email.test(v);
27662         },
27663         /**
27664          * The error text to display when the email validation function returns false
27665          * @type String
27666          */
27667         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
27668         /**
27669          * The keystroke filter mask to be applied on email input
27670          * @type RegExp
27671          */
27672         'emailMask' : /[a-z0-9_\.\-@]/i,
27673
27674         /**
27675          * The function used to validate URLs
27676          * @param {String} value The URL
27677          */
27678         'url' : function(v){
27679             return url.test(v);
27680         },
27681         /**
27682          * The error text to display when the url validation function returns false
27683          * @type String
27684          */
27685         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
27686         
27687         /**
27688          * The function used to validate alpha values
27689          * @param {String} value The value
27690          */
27691         'alpha' : function(v){
27692             return alpha.test(v);
27693         },
27694         /**
27695          * The error text to display when the alpha validation function returns false
27696          * @type String
27697          */
27698         'alphaText' : 'This field should only contain letters and _',
27699         /**
27700          * The keystroke filter mask to be applied on alpha input
27701          * @type RegExp
27702          */
27703         'alphaMask' : /[a-z_]/i,
27704
27705         /**
27706          * The function used to validate alphanumeric values
27707          * @param {String} value The value
27708          */
27709         'alphanum' : function(v){
27710             return alphanum.test(v);
27711         },
27712         /**
27713          * The error text to display when the alphanumeric validation function returns false
27714          * @type String
27715          */
27716         'alphanumText' : 'This field should only contain letters, numbers and _',
27717         /**
27718          * The keystroke filter mask to be applied on alphanumeric input
27719          * @type RegExp
27720          */
27721         'alphanumMask' : /[a-z0-9_]/i
27722     };
27723 }();//<script type="text/javascript">
27724
27725 /**
27726  * @class Roo.form.FCKeditor
27727  * @extends Roo.form.TextArea
27728  * Wrapper around the FCKEditor http://www.fckeditor.net
27729  * @constructor
27730  * Creates a new FCKeditor
27731  * @param {Object} config Configuration options
27732  */
27733 Roo.form.FCKeditor = function(config){
27734     Roo.form.FCKeditor.superclass.constructor.call(this, config);
27735     this.addEvents({
27736          /**
27737          * @event editorinit
27738          * Fired when the editor is initialized - you can add extra handlers here..
27739          * @param {FCKeditor} this
27740          * @param {Object} the FCK object.
27741          */
27742         editorinit : true
27743     });
27744     
27745     
27746 };
27747 Roo.form.FCKeditor.editors = { };
27748 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
27749 {
27750     //defaultAutoCreate : {
27751     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
27752     //},
27753     // private
27754     /**
27755      * @cfg {Object} fck options - see fck manual for details.
27756      */
27757     fckconfig : false,
27758     
27759     /**
27760      * @cfg {Object} fck toolbar set (Basic or Default)
27761      */
27762     toolbarSet : 'Basic',
27763     /**
27764      * @cfg {Object} fck BasePath
27765      */ 
27766     basePath : '/fckeditor/',
27767     
27768     
27769     frame : false,
27770     
27771     value : '',
27772     
27773    
27774     onRender : function(ct, position)
27775     {
27776         if(!this.el){
27777             this.defaultAutoCreate = {
27778                 tag: "textarea",
27779                 style:"width:300px;height:60px;",
27780                 autocomplete: "off"
27781             };
27782         }
27783         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
27784         /*
27785         if(this.grow){
27786             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
27787             if(this.preventScrollbars){
27788                 this.el.setStyle("overflow", "hidden");
27789             }
27790             this.el.setHeight(this.growMin);
27791         }
27792         */
27793         //console.log('onrender' + this.getId() );
27794         Roo.form.FCKeditor.editors[this.getId()] = this;
27795          
27796
27797         this.replaceTextarea() ;
27798         
27799     },
27800     
27801     getEditor : function() {
27802         return this.fckEditor;
27803     },
27804     /**
27805      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
27806      * @param {Mixed} value The value to set
27807      */
27808     
27809     
27810     setValue : function(value)
27811     {
27812         //console.log('setValue: ' + value);
27813         
27814         if(typeof(value) == 'undefined') { // not sure why this is happending...
27815             return;
27816         }
27817         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
27818         
27819         //if(!this.el || !this.getEditor()) {
27820         //    this.value = value;
27821             //this.setValue.defer(100,this,[value]);    
27822         //    return;
27823         //} 
27824         
27825         if(!this.getEditor()) {
27826             return;
27827         }
27828         
27829         this.getEditor().SetData(value);
27830         
27831         //
27832
27833     },
27834
27835     /**
27836      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
27837      * @return {Mixed} value The field value
27838      */
27839     getValue : function()
27840     {
27841         
27842         if (this.frame && this.frame.dom.style.display == 'none') {
27843             return Roo.form.FCKeditor.superclass.getValue.call(this);
27844         }
27845         
27846         if(!this.el || !this.getEditor()) {
27847            
27848            // this.getValue.defer(100,this); 
27849             return this.value;
27850         }
27851        
27852         
27853         var value=this.getEditor().GetData();
27854         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
27855         return Roo.form.FCKeditor.superclass.getValue.call(this);
27856         
27857
27858     },
27859
27860     /**
27861      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
27862      * @return {Mixed} value The field value
27863      */
27864     getRawValue : function()
27865     {
27866         if (this.frame && this.frame.dom.style.display == 'none') {
27867             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
27868         }
27869         
27870         if(!this.el || !this.getEditor()) {
27871             //this.getRawValue.defer(100,this); 
27872             return this.value;
27873             return;
27874         }
27875         
27876         
27877         
27878         var value=this.getEditor().GetData();
27879         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
27880         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
27881          
27882     },
27883     
27884     setSize : function(w,h) {
27885         
27886         
27887         
27888         //if (this.frame && this.frame.dom.style.display == 'none') {
27889         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27890         //    return;
27891         //}
27892         //if(!this.el || !this.getEditor()) {
27893         //    this.setSize.defer(100,this, [w,h]); 
27894         //    return;
27895         //}
27896         
27897         
27898         
27899         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27900         
27901         this.frame.dom.setAttribute('width', w);
27902         this.frame.dom.setAttribute('height', h);
27903         this.frame.setSize(w,h);
27904         
27905     },
27906     
27907     toggleSourceEdit : function(value) {
27908         
27909       
27910          
27911         this.el.dom.style.display = value ? '' : 'none';
27912         this.frame.dom.style.display = value ?  'none' : '';
27913         
27914     },
27915     
27916     
27917     focus: function(tag)
27918     {
27919         if (this.frame.dom.style.display == 'none') {
27920             return Roo.form.FCKeditor.superclass.focus.call(this);
27921         }
27922         if(!this.el || !this.getEditor()) {
27923             this.focus.defer(100,this, [tag]); 
27924             return;
27925         }
27926         
27927         
27928         
27929         
27930         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
27931         this.getEditor().Focus();
27932         if (tgs.length) {
27933             if (!this.getEditor().Selection.GetSelection()) {
27934                 this.focus.defer(100,this, [tag]); 
27935                 return;
27936             }
27937             
27938             
27939             var r = this.getEditor().EditorDocument.createRange();
27940             r.setStart(tgs[0],0);
27941             r.setEnd(tgs[0],0);
27942             this.getEditor().Selection.GetSelection().removeAllRanges();
27943             this.getEditor().Selection.GetSelection().addRange(r);
27944             this.getEditor().Focus();
27945         }
27946         
27947     },
27948     
27949     
27950     
27951     replaceTextarea : function()
27952     {
27953         if ( document.getElementById( this.getId() + '___Frame' ) )
27954             return ;
27955         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
27956         //{
27957             // We must check the elements firstly using the Id and then the name.
27958         var oTextarea = document.getElementById( this.getId() );
27959         
27960         var colElementsByName = document.getElementsByName( this.getId() ) ;
27961          
27962         oTextarea.style.display = 'none' ;
27963
27964         if ( oTextarea.tabIndex ) {            
27965             this.TabIndex = oTextarea.tabIndex ;
27966         }
27967         
27968         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
27969         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
27970         this.frame = Roo.get(this.getId() + '___Frame')
27971     },
27972     
27973     _getConfigHtml : function()
27974     {
27975         var sConfig = '' ;
27976
27977         for ( var o in this.fckconfig ) {
27978             sConfig += sConfig.length > 0  ? '&amp;' : '';
27979             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
27980         }
27981
27982         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
27983     },
27984     
27985     
27986     _getIFrameHtml : function()
27987     {
27988         var sFile = 'fckeditor.html' ;
27989         /* no idea what this is about..
27990         try
27991         {
27992             if ( (/fcksource=true/i).test( window.top.location.search ) )
27993                 sFile = 'fckeditor.original.html' ;
27994         }
27995         catch (e) { 
27996         */
27997
27998         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
27999         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
28000         
28001         
28002         var html = '<iframe id="' + this.getId() +
28003             '___Frame" src="' + sLink +
28004             '" width="' + this.width +
28005             '" height="' + this.height + '"' +
28006             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
28007             ' frameborder="0" scrolling="no"></iframe>' ;
28008
28009         return html ;
28010     },
28011     
28012     _insertHtmlBefore : function( html, element )
28013     {
28014         if ( element.insertAdjacentHTML )       {
28015             // IE
28016             element.insertAdjacentHTML( 'beforeBegin', html ) ;
28017         } else { // Gecko
28018             var oRange = document.createRange() ;
28019             oRange.setStartBefore( element ) ;
28020             var oFragment = oRange.createContextualFragment( html );
28021             element.parentNode.insertBefore( oFragment, element ) ;
28022         }
28023     }
28024     
28025     
28026   
28027     
28028     
28029     
28030     
28031
28032 });
28033
28034 //Roo.reg('fckeditor', Roo.form.FCKeditor);
28035
28036 function FCKeditor_OnComplete(editorInstance){
28037     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
28038     f.fckEditor = editorInstance;
28039     //console.log("loaded");
28040     f.fireEvent('editorinit', f, editorInstance);
28041
28042   
28043
28044  
28045
28046
28047
28048
28049
28050
28051
28052
28053
28054
28055
28056
28057
28058
28059
28060 //<script type="text/javascript">
28061 /**
28062  * @class Roo.form.GridField
28063  * @extends Roo.form.Field
28064  * Embed a grid (or editable grid into a form)
28065  * STATUS ALPHA
28066  * 
28067  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
28068  * it needs 
28069  * xgrid.store = Roo.data.Store
28070  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
28071  * xgrid.store.reader = Roo.data.JsonReader 
28072  * 
28073  * 
28074  * @constructor
28075  * Creates a new GridField
28076  * @param {Object} config Configuration options
28077  */
28078 Roo.form.GridField = function(config){
28079     Roo.form.GridField.superclass.constructor.call(this, config);
28080      
28081 };
28082
28083 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
28084     /**
28085      * @cfg {Number} width  - used to restrict width of grid..
28086      */
28087     width : 100,
28088     /**
28089      * @cfg {Number} height - used to restrict height of grid..
28090      */
28091     height : 50,
28092      /**
28093      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
28094          * 
28095          *}
28096      */
28097     xgrid : false, 
28098     /**
28099      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28100      * {tag: "input", type: "checkbox", autocomplete: "off"})
28101      */
28102    // defaultAutoCreate : { tag: 'div' },
28103     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
28104     /**
28105      * @cfg {String} addTitle Text to include for adding a title.
28106      */
28107     addTitle : false,
28108     //
28109     onResize : function(){
28110         Roo.form.Field.superclass.onResize.apply(this, arguments);
28111     },
28112
28113     initEvents : function(){
28114         // Roo.form.Checkbox.superclass.initEvents.call(this);
28115         // has no events...
28116        
28117     },
28118
28119
28120     getResizeEl : function(){
28121         return this.wrap;
28122     },
28123
28124     getPositionEl : function(){
28125         return this.wrap;
28126     },
28127
28128     // private
28129     onRender : function(ct, position){
28130         
28131         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
28132         var style = this.style;
28133         delete this.style;
28134         
28135         Roo.form.GridField.superclass.onRender.call(this, ct, position);
28136         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
28137         this.viewEl = this.wrap.createChild({ tag: 'div' });
28138         if (style) {
28139             this.viewEl.applyStyles(style);
28140         }
28141         if (this.width) {
28142             this.viewEl.setWidth(this.width);
28143         }
28144         if (this.height) {
28145             this.viewEl.setHeight(this.height);
28146         }
28147         //if(this.inputValue !== undefined){
28148         //this.setValue(this.value);
28149         
28150         
28151         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
28152         
28153         
28154         this.grid.render();
28155         this.grid.getDataSource().on('remove', this.refreshValue, this);
28156         this.grid.getDataSource().on('update', this.refreshValue, this);
28157         this.grid.on('afteredit', this.refreshValue, this);
28158  
28159     },
28160      
28161     
28162     /**
28163      * Sets the value of the item. 
28164      * @param {String} either an object  or a string..
28165      */
28166     setValue : function(v){
28167         //this.value = v;
28168         v = v || []; // empty set..
28169         // this does not seem smart - it really only affects memoryproxy grids..
28170         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
28171             var ds = this.grid.getDataSource();
28172             // assumes a json reader..
28173             var data = {}
28174             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
28175             ds.loadData( data);
28176         }
28177         Roo.form.GridField.superclass.setValue.call(this, v);
28178         this.refreshValue();
28179         // should load data in the grid really....
28180     },
28181     
28182     // private
28183     refreshValue: function() {
28184          var val = [];
28185         this.grid.getDataSource().each(function(r) {
28186             val.push(r.data);
28187         });
28188         this.el.dom.value = Roo.encode(val);
28189     }
28190     
28191      
28192     
28193     
28194 });/*
28195  * Based on:
28196  * Ext JS Library 1.1.1
28197  * Copyright(c) 2006-2007, Ext JS, LLC.
28198  *
28199  * Originally Released Under LGPL - original licence link has changed is not relivant.
28200  *
28201  * Fork - LGPL
28202  * <script type="text/javascript">
28203  */
28204 /**
28205  * @class Roo.form.DisplayField
28206  * @extends Roo.form.Field
28207  * A generic Field to display non-editable data.
28208  * @constructor
28209  * Creates a new Display Field item.
28210  * @param {Object} config Configuration options
28211  */
28212 Roo.form.DisplayField = function(config){
28213     Roo.form.DisplayField.superclass.constructor.call(this, config);
28214     
28215 };
28216
28217 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
28218     inputType:      'hidden',
28219     allowBlank:     true,
28220     readOnly:         true,
28221     
28222  
28223     /**
28224      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
28225      */
28226     focusClass : undefined,
28227     /**
28228      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
28229      */
28230     fieldClass: 'x-form-field',
28231     
28232      /**
28233      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
28234      */
28235     valueRenderer: undefined,
28236     
28237     width: 100,
28238     /**
28239      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28240      * {tag: "input", type: "checkbox", autocomplete: "off"})
28241      */
28242      
28243  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
28244
28245     onResize : function(){
28246         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
28247         
28248     },
28249
28250     initEvents : function(){
28251         // Roo.form.Checkbox.superclass.initEvents.call(this);
28252         // has no events...
28253        
28254     },
28255
28256
28257     getResizeEl : function(){
28258         return this.wrap;
28259     },
28260
28261     getPositionEl : function(){
28262         return this.wrap;
28263     },
28264
28265     // private
28266     onRender : function(ct, position){
28267         
28268         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
28269         //if(this.inputValue !== undefined){
28270         this.wrap = this.el.wrap();
28271         
28272         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
28273         
28274         if (this.bodyStyle) {
28275             this.viewEl.applyStyles(this.bodyStyle);
28276         }
28277         //this.viewEl.setStyle('padding', '2px');
28278         
28279         this.setValue(this.value);
28280         
28281     },
28282 /*
28283     // private
28284     initValue : Roo.emptyFn,
28285
28286   */
28287
28288         // private
28289     onClick : function(){
28290         
28291     },
28292
28293     /**
28294      * Sets the checked state of the checkbox.
28295      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
28296      */
28297     setValue : function(v){
28298         this.value = v;
28299         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
28300         // this might be called before we have a dom element..
28301         if (!this.viewEl) {
28302             return;
28303         }
28304         this.viewEl.dom.innerHTML = html;
28305         Roo.form.DisplayField.superclass.setValue.call(this, v);
28306
28307     }
28308 });/*
28309  * 
28310  * Licence- LGPL
28311  * 
28312  */
28313
28314 /**
28315  * @class Roo.form.DayPicker
28316  * @extends Roo.form.Field
28317  * A Day picker show [M] [T] [W] ....
28318  * @constructor
28319  * Creates a new Day Picker
28320  * @param {Object} config Configuration options
28321  */
28322 Roo.form.DayPicker= function(config){
28323     Roo.form.DayPicker.superclass.constructor.call(this, config);
28324      
28325 };
28326
28327 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
28328     /**
28329      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
28330      */
28331     focusClass : undefined,
28332     /**
28333      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
28334      */
28335     fieldClass: "x-form-field",
28336    
28337     /**
28338      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28339      * {tag: "input", type: "checkbox", autocomplete: "off"})
28340      */
28341     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
28342     
28343    
28344     actionMode : 'viewEl', 
28345     //
28346     // private
28347  
28348     inputType : 'hidden',
28349     
28350      
28351     inputElement: false, // real input element?
28352     basedOn: false, // ????
28353     
28354     isFormField: true, // not sure where this is needed!!!!
28355
28356     onResize : function(){
28357         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
28358         if(!this.boxLabel){
28359             this.el.alignTo(this.wrap, 'c-c');
28360         }
28361     },
28362
28363     initEvents : function(){
28364         Roo.form.Checkbox.superclass.initEvents.call(this);
28365         this.el.on("click", this.onClick,  this);
28366         this.el.on("change", this.onClick,  this);
28367     },
28368
28369
28370     getResizeEl : function(){
28371         return this.wrap;
28372     },
28373
28374     getPositionEl : function(){
28375         return this.wrap;
28376     },
28377
28378     
28379     // private
28380     onRender : function(ct, position){
28381         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
28382        
28383         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
28384         
28385         var r1 = '<table><tr>';
28386         var r2 = '<tr class="x-form-daypick-icons">';
28387         for (var i=0; i < 7; i++) {
28388             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
28389             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
28390         }
28391         
28392         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
28393         viewEl.select('img').on('click', this.onClick, this);
28394         this.viewEl = viewEl;   
28395         
28396         
28397         // this will not work on Chrome!!!
28398         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
28399         this.el.on('propertychange', this.setFromHidden,  this);  //ie
28400         
28401         
28402           
28403
28404     },
28405
28406     // private
28407     initValue : Roo.emptyFn,
28408
28409     /**
28410      * Returns the checked state of the checkbox.
28411      * @return {Boolean} True if checked, else false
28412      */
28413     getValue : function(){
28414         return this.el.dom.value;
28415         
28416     },
28417
28418         // private
28419     onClick : function(e){ 
28420         //this.setChecked(!this.checked);
28421         Roo.get(e.target).toggleClass('x-menu-item-checked');
28422         this.refreshValue();
28423         //if(this.el.dom.checked != this.checked){
28424         //    this.setValue(this.el.dom.checked);
28425        // }
28426     },
28427     
28428     // private
28429     refreshValue : function()
28430     {
28431         var val = '';
28432         this.viewEl.select('img',true).each(function(e,i,n)  {
28433             val += e.is(".x-menu-item-checked") ? String(n) : '';
28434         });
28435         this.setValue(val, true);
28436     },
28437
28438     /**
28439      * Sets the checked state of the checkbox.
28440      * On is always based on a string comparison between inputValue and the param.
28441      * @param {Boolean/String} value - the value to set 
28442      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
28443      */
28444     setValue : function(v,suppressEvent){
28445         if (!this.el.dom) {
28446             return;
28447         }
28448         var old = this.el.dom.value ;
28449         this.el.dom.value = v;
28450         if (suppressEvent) {
28451             return ;
28452         }
28453          
28454         // update display..
28455         this.viewEl.select('img',true).each(function(e,i,n)  {
28456             
28457             var on = e.is(".x-menu-item-checked");
28458             var newv = v.indexOf(String(n)) > -1;
28459             if (on != newv) {
28460                 e.toggleClass('x-menu-item-checked');
28461             }
28462             
28463         });
28464         
28465         
28466         this.fireEvent('change', this, v, old);
28467         
28468         
28469     },
28470    
28471     // handle setting of hidden value by some other method!!?!?
28472     setFromHidden: function()
28473     {
28474         if(!this.el){
28475             return;
28476         }
28477         //console.log("SET FROM HIDDEN");
28478         //alert('setFrom hidden');
28479         this.setValue(this.el.dom.value);
28480     },
28481     
28482     onDestroy : function()
28483     {
28484         if(this.viewEl){
28485             Roo.get(this.viewEl).remove();
28486         }
28487          
28488         Roo.form.DayPicker.superclass.onDestroy.call(this);
28489     }
28490
28491 });//<script type="text/javasscript">
28492  
28493
28494 /**
28495  * @class Roo.DDView
28496  * A DnD enabled version of Roo.View.
28497  * @param {Element/String} container The Element in which to create the View.
28498  * @param {String} tpl The template string used to create the markup for each element of the View
28499  * @param {Object} config The configuration properties. These include all the config options of
28500  * {@link Roo.View} plus some specific to this class.<br>
28501  * <p>
28502  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28503  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28504  * <p>
28505  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28506 .x-view-drag-insert-above {
28507         border-top:1px dotted #3366cc;
28508 }
28509 .x-view-drag-insert-below {
28510         border-bottom:1px dotted #3366cc;
28511 }
28512 </code></pre>
28513  * 
28514  */
28515  
28516 Roo.DDView = function(container, tpl, config) {
28517     Roo.DDView.superclass.constructor.apply(this, arguments);
28518     this.getEl().setStyle("outline", "0px none");
28519     this.getEl().unselectable();
28520     if (this.dragGroup) {
28521                 this.setDraggable(this.dragGroup.split(","));
28522     }
28523     if (this.dropGroup) {
28524                 this.setDroppable(this.dropGroup.split(","));
28525     }
28526     if (this.deletable) {
28527         this.setDeletable();
28528     }
28529     this.isDirtyFlag = false;
28530         this.addEvents({
28531                 "drop" : true
28532         });
28533 };
28534
28535 Roo.extend(Roo.DDView, Roo.View, {
28536 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28537 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28538 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28539 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28540
28541         isFormField: true,
28542
28543         reset: Roo.emptyFn,
28544         
28545         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28546
28547         validate: function() {
28548                 return true;
28549         },
28550         
28551         destroy: function() {
28552                 this.purgeListeners();
28553                 this.getEl.removeAllListeners();
28554                 this.getEl().remove();
28555                 if (this.dragZone) {
28556                         if (this.dragZone.destroy) {
28557                                 this.dragZone.destroy();
28558                         }
28559                 }
28560                 if (this.dropZone) {
28561                         if (this.dropZone.destroy) {
28562                                 this.dropZone.destroy();
28563                         }
28564                 }
28565         },
28566
28567 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28568         getName: function() {
28569                 return this.name;
28570         },
28571
28572 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28573         setValue: function(v) {
28574                 if (!this.store) {
28575                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28576                 }
28577                 var data = {};
28578                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28579                 this.store.proxy = new Roo.data.MemoryProxy(data);
28580                 this.store.load();
28581         },
28582
28583 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28584         getValue: function() {
28585                 var result = '(';
28586                 this.store.each(function(rec) {
28587                         result += rec.id + ',';
28588                 });
28589                 return result.substr(0, result.length - 1) + ')';
28590         },
28591         
28592         getIds: function() {
28593                 var i = 0, result = new Array(this.store.getCount());
28594                 this.store.each(function(rec) {
28595                         result[i++] = rec.id;
28596                 });
28597                 return result;
28598         },
28599         
28600         isDirty: function() {
28601                 return this.isDirtyFlag;
28602         },
28603
28604 /**
28605  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28606  *      whole Element becomes the target, and this causes the drop gesture to append.
28607  */
28608     getTargetFromEvent : function(e) {
28609                 var target = e.getTarget();
28610                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28611                 target = target.parentNode;
28612                 }
28613                 if (!target) {
28614                         target = this.el.dom.lastChild || this.el.dom;
28615                 }
28616                 return target;
28617     },
28618
28619 /**
28620  *      Create the drag data which consists of an object which has the property "ddel" as
28621  *      the drag proxy element. 
28622  */
28623     getDragData : function(e) {
28624         var target = this.findItemFromChild(e.getTarget());
28625                 if(target) {
28626                         this.handleSelection(e);
28627                         var selNodes = this.getSelectedNodes();
28628             var dragData = {
28629                 source: this,
28630                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28631                 nodes: selNodes,
28632                 records: []
28633                         };
28634                         var selectedIndices = this.getSelectedIndexes();
28635                         for (var i = 0; i < selectedIndices.length; i++) {
28636                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28637                         }
28638                         if (selNodes.length == 1) {
28639                                 dragData.ddel = target.cloneNode(true); // the div element
28640                         } else {
28641                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28642                                 div.className = 'multi-proxy';
28643                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28644                                         div.appendChild(selNodes[i].cloneNode(true));
28645                                 }
28646                                 dragData.ddel = div;
28647                         }
28648             //console.log(dragData)
28649             //console.log(dragData.ddel.innerHTML)
28650                         return dragData;
28651                 }
28652         //console.log('nodragData')
28653                 return false;
28654     },
28655     
28656 /**     Specify to which ddGroup items in this DDView may be dragged. */
28657     setDraggable: function(ddGroup) {
28658         if (ddGroup instanceof Array) {
28659                 Roo.each(ddGroup, this.setDraggable, this);
28660                 return;
28661         }
28662         if (this.dragZone) {
28663                 this.dragZone.addToGroup(ddGroup);
28664         } else {
28665                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28666                                 containerScroll: true,
28667                                 ddGroup: ddGroup 
28668
28669                         });
28670 //                      Draggability implies selection. DragZone's mousedown selects the element.
28671                         if (!this.multiSelect) { this.singleSelect = true; }
28672
28673 //                      Wire the DragZone's handlers up to methods in *this*
28674                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28675                 }
28676     },
28677
28678 /**     Specify from which ddGroup this DDView accepts drops. */
28679     setDroppable: function(ddGroup) {
28680         if (ddGroup instanceof Array) {
28681                 Roo.each(ddGroup, this.setDroppable, this);
28682                 return;
28683         }
28684         if (this.dropZone) {
28685                 this.dropZone.addToGroup(ddGroup);
28686         } else {
28687                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28688                                 containerScroll: true,
28689                                 ddGroup: ddGroup
28690                         });
28691
28692 //                      Wire the DropZone's handlers up to methods in *this*
28693                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28694                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28695                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28696                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28697                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28698                 }
28699     },
28700
28701 /**     Decide whether to drop above or below a View node. */
28702     getDropPoint : function(e, n, dd){
28703         if (n == this.el.dom) { return "above"; }
28704                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28705                 var c = t + (b - t) / 2;
28706                 var y = Roo.lib.Event.getPageY(e);
28707                 if(y <= c) {
28708                         return "above";
28709                 }else{
28710                         return "below";
28711                 }
28712     },
28713
28714     onNodeEnter : function(n, dd, e, data){
28715                 return false;
28716     },
28717     
28718     onNodeOver : function(n, dd, e, data){
28719                 var pt = this.getDropPoint(e, n, dd);
28720                 // set the insert point style on the target node
28721                 var dragElClass = this.dropNotAllowed;
28722                 if (pt) {
28723                         var targetElClass;
28724                         if (pt == "above"){
28725                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28726                                 targetElClass = "x-view-drag-insert-above";
28727                         } else {
28728                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28729                                 targetElClass = "x-view-drag-insert-below";
28730                         }
28731                         if (this.lastInsertClass != targetElClass){
28732                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28733                                 this.lastInsertClass = targetElClass;
28734                         }
28735                 }
28736                 return dragElClass;
28737         },
28738
28739     onNodeOut : function(n, dd, e, data){
28740                 this.removeDropIndicators(n);
28741     },
28742
28743     onNodeDrop : function(n, dd, e, data){
28744         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28745                 return false;
28746         }
28747         var pt = this.getDropPoint(e, n, dd);
28748                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28749                 if (pt == "below") { insertAt++; }
28750                 for (var i = 0; i < data.records.length; i++) {
28751                         var r = data.records[i];
28752                         var dup = this.store.getById(r.id);
28753                         if (dup && (dd != this.dragZone)) {
28754                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28755                         } else {
28756                                 if (data.copy) {
28757                                         this.store.insert(insertAt++, r.copy());
28758                                 } else {
28759                                         data.source.isDirtyFlag = true;
28760                                         r.store.remove(r);
28761                                         this.store.insert(insertAt++, r);
28762                                 }
28763                                 this.isDirtyFlag = true;
28764                         }
28765                 }
28766                 this.dragZone.cachedTarget = null;
28767                 return true;
28768     },
28769
28770     removeDropIndicators : function(n){
28771                 if(n){
28772                         Roo.fly(n).removeClass([
28773                                 "x-view-drag-insert-above",
28774                                 "x-view-drag-insert-below"]);
28775                         this.lastInsertClass = "_noclass";
28776                 }
28777     },
28778
28779 /**
28780  *      Utility method. Add a delete option to the DDView's context menu.
28781  *      @param {String} imageUrl The URL of the "delete" icon image.
28782  */
28783         setDeletable: function(imageUrl) {
28784                 if (!this.singleSelect && !this.multiSelect) {
28785                         this.singleSelect = true;
28786                 }
28787                 var c = this.getContextMenu();
28788                 this.contextMenu.on("itemclick", function(item) {
28789                         switch (item.id) {
28790                                 case "delete":
28791                                         this.remove(this.getSelectedIndexes());
28792                                         break;
28793                         }
28794                 }, this);
28795                 this.contextMenu.add({
28796                         icon: imageUrl,
28797                         id: "delete",
28798                         text: 'Delete'
28799                 });
28800         },
28801         
28802 /**     Return the context menu for this DDView. */
28803         getContextMenu: function() {
28804                 if (!this.contextMenu) {
28805 //                      Create the View's context menu
28806                         this.contextMenu = new Roo.menu.Menu({
28807                                 id: this.id + "-contextmenu"
28808                         });
28809                         this.el.on("contextmenu", this.showContextMenu, this);
28810                 }
28811                 return this.contextMenu;
28812         },
28813         
28814         disableContextMenu: function() {
28815                 if (this.contextMenu) {
28816                         this.el.un("contextmenu", this.showContextMenu, this);
28817                 }
28818         },
28819
28820         showContextMenu: function(e, item) {
28821         item = this.findItemFromChild(e.getTarget());
28822                 if (item) {
28823                         e.stopEvent();
28824                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28825                         this.contextMenu.showAt(e.getXY());
28826             }
28827     },
28828
28829 /**
28830  *      Remove {@link Roo.data.Record}s at the specified indices.
28831  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28832  */
28833     remove: function(selectedIndices) {
28834                 selectedIndices = [].concat(selectedIndices);
28835                 for (var i = 0; i < selectedIndices.length; i++) {
28836                         var rec = this.store.getAt(selectedIndices[i]);
28837                         this.store.remove(rec);
28838                 }
28839     },
28840
28841 /**
28842  *      Double click fires the event, but also, if this is draggable, and there is only one other
28843  *      related DropZone, it transfers the selected node.
28844  */
28845     onDblClick : function(e){
28846         var item = this.findItemFromChild(e.getTarget());
28847         if(item){
28848             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28849                 return false;
28850             }
28851             if (this.dragGroup) {
28852                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28853                     while (targets.indexOf(this.dropZone) > -1) {
28854                             targets.remove(this.dropZone);
28855                                 }
28856                     if (targets.length == 1) {
28857                                         this.dragZone.cachedTarget = null;
28858                         var el = Roo.get(targets[0].getEl());
28859                         var box = el.getBox(true);
28860                         targets[0].onNodeDrop(el.dom, {
28861                                 target: el.dom,
28862                                 xy: [box.x, box.y + box.height - 1]
28863                         }, null, this.getDragData(e));
28864                     }
28865                 }
28866         }
28867     },
28868     
28869     handleSelection: function(e) {
28870                 this.dragZone.cachedTarget = null;
28871         var item = this.findItemFromChild(e.getTarget());
28872         if (!item) {
28873                 this.clearSelections(true);
28874                 return;
28875         }
28876                 if (item && (this.multiSelect || this.singleSelect)){
28877                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28878                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28879                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28880                                 this.unselect(item);
28881                         } else {
28882                                 this.select(item, this.multiSelect && e.ctrlKey);
28883                                 this.lastSelection = item;
28884                         }
28885                 }
28886     },
28887
28888     onItemClick : function(item, index, e){
28889                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28890                         return false;
28891                 }
28892                 return true;
28893     },
28894
28895     unselect : function(nodeInfo, suppressEvent){
28896                 var node = this.getNode(nodeInfo);
28897                 if(node && this.isSelected(node)){
28898                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28899                                 Roo.fly(node).removeClass(this.selectedClass);
28900                                 this.selections.remove(node);
28901                                 if(!suppressEvent){
28902                                         this.fireEvent("selectionchange", this, this.selections);
28903                                 }
28904                         }
28905                 }
28906     }
28907 });
28908 /*
28909  * Based on:
28910  * Ext JS Library 1.1.1
28911  * Copyright(c) 2006-2007, Ext JS, LLC.
28912  *
28913  * Originally Released Under LGPL - original licence link has changed is not relivant.
28914  *
28915  * Fork - LGPL
28916  * <script type="text/javascript">
28917  */
28918  
28919 /**
28920  * @class Roo.LayoutManager
28921  * @extends Roo.util.Observable
28922  * Base class for layout managers.
28923  */
28924 Roo.LayoutManager = function(container, config){
28925     Roo.LayoutManager.superclass.constructor.call(this);
28926     this.el = Roo.get(container);
28927     // ie scrollbar fix
28928     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28929         document.body.scroll = "no";
28930     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28931         this.el.position('relative');
28932     }
28933     this.id = this.el.id;
28934     this.el.addClass("x-layout-container");
28935     /** false to disable window resize monitoring @type Boolean */
28936     this.monitorWindowResize = true;
28937     this.regions = {};
28938     this.addEvents({
28939         /**
28940          * @event layout
28941          * Fires when a layout is performed. 
28942          * @param {Roo.LayoutManager} this
28943          */
28944         "layout" : true,
28945         /**
28946          * @event regionresized
28947          * Fires when the user resizes a region. 
28948          * @param {Roo.LayoutRegion} region The resized region
28949          * @param {Number} newSize The new size (width for east/west, height for north/south)
28950          */
28951         "regionresized" : true,
28952         /**
28953          * @event regioncollapsed
28954          * Fires when a region is collapsed. 
28955          * @param {Roo.LayoutRegion} region The collapsed region
28956          */
28957         "regioncollapsed" : true,
28958         /**
28959          * @event regionexpanded
28960          * Fires when a region is expanded.  
28961          * @param {Roo.LayoutRegion} region The expanded region
28962          */
28963         "regionexpanded" : true
28964     });
28965     this.updating = false;
28966     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28967 };
28968
28969 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28970     /**
28971      * Returns true if this layout is currently being updated
28972      * @return {Boolean}
28973      */
28974     isUpdating : function(){
28975         return this.updating; 
28976     },
28977     
28978     /**
28979      * Suspend the LayoutManager from doing auto-layouts while
28980      * making multiple add or remove calls
28981      */
28982     beginUpdate : function(){
28983         this.updating = true;    
28984     },
28985     
28986     /**
28987      * Restore auto-layouts and optionally disable the manager from performing a layout
28988      * @param {Boolean} noLayout true to disable a layout update 
28989      */
28990     endUpdate : function(noLayout){
28991         this.updating = false;
28992         if(!noLayout){
28993             this.layout();
28994         }    
28995     },
28996     
28997     layout: function(){
28998         
28999     },
29000     
29001     onRegionResized : function(region, newSize){
29002         this.fireEvent("regionresized", region, newSize);
29003         this.layout();
29004     },
29005     
29006     onRegionCollapsed : function(region){
29007         this.fireEvent("regioncollapsed", region);
29008     },
29009     
29010     onRegionExpanded : function(region){
29011         this.fireEvent("regionexpanded", region);
29012     },
29013         
29014     /**
29015      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
29016      * performs box-model adjustments.
29017      * @return {Object} The size as an object {width: (the width), height: (the height)}
29018      */
29019     getViewSize : function(){
29020         var size;
29021         if(this.el.dom != document.body){
29022             size = this.el.getSize();
29023         }else{
29024             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
29025         }
29026         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
29027         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
29028         return size;
29029     },
29030     
29031     /**
29032      * Returns the Element this layout is bound to.
29033      * @return {Roo.Element}
29034      */
29035     getEl : function(){
29036         return this.el;
29037     },
29038     
29039     /**
29040      * Returns the specified region.
29041      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
29042      * @return {Roo.LayoutRegion}
29043      */
29044     getRegion : function(target){
29045         return this.regions[target.toLowerCase()];
29046     },
29047     
29048     onWindowResize : function(){
29049         if(this.monitorWindowResize){
29050             this.layout();
29051         }
29052     }
29053 });/*
29054  * Based on:
29055  * Ext JS Library 1.1.1
29056  * Copyright(c) 2006-2007, Ext JS, LLC.
29057  *
29058  * Originally Released Under LGPL - original licence link has changed is not relivant.
29059  *
29060  * Fork - LGPL
29061  * <script type="text/javascript">
29062  */
29063 /**
29064  * @class Roo.BorderLayout
29065  * @extends Roo.LayoutManager
29066  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
29067  * please see: <br><br>
29068  * <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>
29069  * <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>
29070  * Example:
29071  <pre><code>
29072  var layout = new Roo.BorderLayout(document.body, {
29073     north: {
29074         initialSize: 25,
29075         titlebar: false
29076     },
29077     west: {
29078         split:true,
29079         initialSize: 200,
29080         minSize: 175,
29081         maxSize: 400,
29082         titlebar: true,
29083         collapsible: true
29084     },
29085     east: {
29086         split:true,
29087         initialSize: 202,
29088         minSize: 175,
29089         maxSize: 400,
29090         titlebar: true,
29091         collapsible: true
29092     },
29093     south: {
29094         split:true,
29095         initialSize: 100,
29096         minSize: 100,
29097         maxSize: 200,
29098         titlebar: true,
29099         collapsible: true
29100     },
29101     center: {
29102         titlebar: true,
29103         autoScroll:true,
29104         resizeTabs: true,
29105         minTabWidth: 50,
29106         preferredTabWidth: 150
29107     }
29108 });
29109
29110 // shorthand
29111 var CP = Roo.ContentPanel;
29112
29113 layout.beginUpdate();
29114 layout.add("north", new CP("north", "North"));
29115 layout.add("south", new CP("south", {title: "South", closable: true}));
29116 layout.add("west", new CP("west", {title: "West"}));
29117 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
29118 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
29119 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
29120 layout.getRegion("center").showPanel("center1");
29121 layout.endUpdate();
29122 </code></pre>
29123
29124 <b>The container the layout is rendered into can be either the body element or any other element.
29125 If it is not the body element, the container needs to either be an absolute positioned element,
29126 or you will need to add "position:relative" to the css of the container.  You will also need to specify
29127 the container size if it is not the body element.</b>
29128
29129 * @constructor
29130 * Create a new BorderLayout
29131 * @param {String/HTMLElement/Element} container The container this layout is bound to
29132 * @param {Object} config Configuration options
29133  */
29134 Roo.BorderLayout = function(container, config){
29135     config = config || {};
29136     Roo.BorderLayout.superclass.constructor.call(this, container, config);
29137     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
29138     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
29139         var target = this.factory.validRegions[i];
29140         if(config[target]){
29141             this.addRegion(target, config[target]);
29142         }
29143     }
29144 };
29145
29146 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
29147     /**
29148      * Creates and adds a new region if it doesn't already exist.
29149      * @param {String} target The target region key (north, south, east, west or center).
29150      * @param {Object} config The regions config object
29151      * @return {BorderLayoutRegion} The new region
29152      */
29153     addRegion : function(target, config){
29154         if(!this.regions[target]){
29155             var r = this.factory.create(target, this, config);
29156             this.bindRegion(target, r);
29157         }
29158         return this.regions[target];
29159     },
29160
29161     // private (kinda)
29162     bindRegion : function(name, r){
29163         this.regions[name] = r;
29164         r.on("visibilitychange", this.layout, this);
29165         r.on("paneladded", this.layout, this);
29166         r.on("panelremoved", this.layout, this);
29167         r.on("invalidated", this.layout, this);
29168         r.on("resized", this.onRegionResized, this);
29169         r.on("collapsed", this.onRegionCollapsed, this);
29170         r.on("expanded", this.onRegionExpanded, this);
29171     },
29172
29173     /**
29174      * Performs a layout update.
29175      */
29176     layout : function(){
29177         if(this.updating) return;
29178         var size = this.getViewSize();
29179         var w = size.width;
29180         var h = size.height;
29181         var centerW = w;
29182         var centerH = h;
29183         var centerY = 0;
29184         var centerX = 0;
29185         //var x = 0, y = 0;
29186
29187         var rs = this.regions;
29188         var north = rs["north"];
29189         var south = rs["south"]; 
29190         var west = rs["west"];
29191         var east = rs["east"];
29192         var center = rs["center"];
29193         //if(this.hideOnLayout){ // not supported anymore
29194             //c.el.setStyle("display", "none");
29195         //}
29196         if(north && north.isVisible()){
29197             var b = north.getBox();
29198             var m = north.getMargins();
29199             b.width = w - (m.left+m.right);
29200             b.x = m.left;
29201             b.y = m.top;
29202             centerY = b.height + b.y + m.bottom;
29203             centerH -= centerY;
29204             north.updateBox(this.safeBox(b));
29205         }
29206         if(south && south.isVisible()){
29207             var b = south.getBox();
29208             var m = south.getMargins();
29209             b.width = w - (m.left+m.right);
29210             b.x = m.left;
29211             var totalHeight = (b.height + m.top + m.bottom);
29212             b.y = h - totalHeight + m.top;
29213             centerH -= totalHeight;
29214             south.updateBox(this.safeBox(b));
29215         }
29216         if(west && west.isVisible()){
29217             var b = west.getBox();
29218             var m = west.getMargins();
29219             b.height = centerH - (m.top+m.bottom);
29220             b.x = m.left;
29221             b.y = centerY + m.top;
29222             var totalWidth = (b.width + m.left + m.right);
29223             centerX += totalWidth;
29224             centerW -= totalWidth;
29225             west.updateBox(this.safeBox(b));
29226         }
29227         if(east && east.isVisible()){
29228             var b = east.getBox();
29229             var m = east.getMargins();
29230             b.height = centerH - (m.top+m.bottom);
29231             var totalWidth = (b.width + m.left + m.right);
29232             b.x = w - totalWidth + m.left;
29233             b.y = centerY + m.top;
29234             centerW -= totalWidth;
29235             east.updateBox(this.safeBox(b));
29236         }
29237         if(center){
29238             var m = center.getMargins();
29239             var centerBox = {
29240                 x: centerX + m.left,
29241                 y: centerY + m.top,
29242                 width: centerW - (m.left+m.right),
29243                 height: centerH - (m.top+m.bottom)
29244             };
29245             //if(this.hideOnLayout){
29246                 //center.el.setStyle("display", "block");
29247             //}
29248             center.updateBox(this.safeBox(centerBox));
29249         }
29250         this.el.repaint();
29251         this.fireEvent("layout", this);
29252     },
29253
29254     // private
29255     safeBox : function(box){
29256         box.width = Math.max(0, box.width);
29257         box.height = Math.max(0, box.height);
29258         return box;
29259     },
29260
29261     /**
29262      * Adds a ContentPanel (or subclass) to this layout.
29263      * @param {String} target The target region key (north, south, east, west or center).
29264      * @param {Roo.ContentPanel} panel The panel to add
29265      * @return {Roo.ContentPanel} The added panel
29266      */
29267     add : function(target, panel){
29268          
29269         target = target.toLowerCase();
29270         return this.regions[target].add(panel);
29271     },
29272
29273     /**
29274      * Remove a ContentPanel (or subclass) to this layout.
29275      * @param {String} target The target region key (north, south, east, west or center).
29276      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
29277      * @return {Roo.ContentPanel} The removed panel
29278      */
29279     remove : function(target, panel){
29280         target = target.toLowerCase();
29281         return this.regions[target].remove(panel);
29282     },
29283
29284     /**
29285      * Searches all regions for a panel with the specified id
29286      * @param {String} panelId
29287      * @return {Roo.ContentPanel} The panel or null if it wasn't found
29288      */
29289     findPanel : function(panelId){
29290         var rs = this.regions;
29291         for(var target in rs){
29292             if(typeof rs[target] != "function"){
29293                 var p = rs[target].getPanel(panelId);
29294                 if(p){
29295                     return p;
29296                 }
29297             }
29298         }
29299         return null;
29300     },
29301
29302     /**
29303      * Searches all regions for a panel with the specified id and activates (shows) it.
29304      * @param {String/ContentPanel} panelId The panels id or the panel itself
29305      * @return {Roo.ContentPanel} The shown panel or null
29306      */
29307     showPanel : function(panelId) {
29308       var rs = this.regions;
29309       for(var target in rs){
29310          var r = rs[target];
29311          if(typeof r != "function"){
29312             if(r.hasPanel(panelId)){
29313                return r.showPanel(panelId);
29314             }
29315          }
29316       }
29317       return null;
29318    },
29319
29320    /**
29321      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
29322      * @param {Roo.state.Provider} provider (optional) An alternate state provider
29323      */
29324     restoreState : function(provider){
29325         if(!provider){
29326             provider = Roo.state.Manager;
29327         }
29328         var sm = new Roo.LayoutStateManager();
29329         sm.init(this, provider);
29330     },
29331
29332     /**
29333      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
29334      * object should contain properties for each region to add ContentPanels to, and each property's value should be
29335      * a valid ContentPanel config object.  Example:
29336      * <pre><code>
29337 // Create the main layout
29338 var layout = new Roo.BorderLayout('main-ct', {
29339     west: {
29340         split:true,
29341         minSize: 175,
29342         titlebar: true
29343     },
29344     center: {
29345         title:'Components'
29346     }
29347 }, 'main-ct');
29348
29349 // Create and add multiple ContentPanels at once via configs
29350 layout.batchAdd({
29351    west: {
29352        id: 'source-files',
29353        autoCreate:true,
29354        title:'Ext Source Files',
29355        autoScroll:true,
29356        fitToFrame:true
29357    },
29358    center : {
29359        el: cview,
29360        autoScroll:true,
29361        fitToFrame:true,
29362        toolbar: tb,
29363        resizeEl:'cbody'
29364    }
29365 });
29366 </code></pre>
29367      * @param {Object} regions An object containing ContentPanel configs by region name
29368      */
29369     batchAdd : function(regions){
29370         this.beginUpdate();
29371         for(var rname in regions){
29372             var lr = this.regions[rname];
29373             if(lr){
29374                 this.addTypedPanels(lr, regions[rname]);
29375             }
29376         }
29377         this.endUpdate();
29378     },
29379
29380     // private
29381     addTypedPanels : function(lr, ps){
29382         if(typeof ps == 'string'){
29383             lr.add(new Roo.ContentPanel(ps));
29384         }
29385         else if(ps instanceof Array){
29386             for(var i =0, len = ps.length; i < len; i++){
29387                 this.addTypedPanels(lr, ps[i]);
29388             }
29389         }
29390         else if(!ps.events){ // raw config?
29391             var el = ps.el;
29392             delete ps.el; // prevent conflict
29393             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29394         }
29395         else {  // panel object assumed!
29396             lr.add(ps);
29397         }
29398     },
29399     /**
29400      * Adds a xtype elements to the layout.
29401      * <pre><code>
29402
29403 layout.addxtype({
29404        xtype : 'ContentPanel',
29405        region: 'west',
29406        items: [ .... ]
29407    }
29408 );
29409
29410 layout.addxtype({
29411         xtype : 'NestedLayoutPanel',
29412         region: 'west',
29413         layout: {
29414            center: { },
29415            west: { }   
29416         },
29417         items : [ ... list of content panels or nested layout panels.. ]
29418    }
29419 );
29420 </code></pre>
29421      * @param {Object} cfg Xtype definition of item to add.
29422      */
29423     addxtype : function(cfg)
29424     {
29425         // basically accepts a pannel...
29426         // can accept a layout region..!?!?
29427        // console.log('BorderLayout add ' + cfg.xtype)
29428         
29429         if (!cfg.xtype.match(/Panel$/)) {
29430             return false;
29431         }
29432         var ret = false;
29433         var region = cfg.region;
29434         delete cfg.region;
29435         
29436           
29437         var xitems = [];
29438         if (cfg.items) {
29439             xitems = cfg.items;
29440             delete cfg.items;
29441         }
29442         
29443         
29444         switch(cfg.xtype) 
29445         {
29446             case 'ContentPanel':  // ContentPanel (el, cfg)
29447             case 'ScrollPanel':  // ContentPanel (el, cfg)
29448                 if(cfg.autoCreate) {
29449                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29450                 } else {
29451                     var el = this.el.createChild();
29452                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29453                 }
29454                 
29455                 this.add(region, ret);
29456                 break;
29457             
29458             
29459             case 'TreePanel': // our new panel!
29460                 cfg.el = this.el.createChild();
29461                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29462                 this.add(region, ret);
29463                 break;
29464             
29465             case 'NestedLayoutPanel': 
29466                 // create a new Layout (which is  a Border Layout...
29467                 var el = this.el.createChild();
29468                 var clayout = cfg.layout;
29469                 delete cfg.layout;
29470                 clayout.items   = clayout.items  || [];
29471                 // replace this exitems with the clayout ones..
29472                 xitems = clayout.items;
29473                  
29474                 
29475                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29476                     cfg.background = false;
29477                 }
29478                 var layout = new Roo.BorderLayout(el, clayout);
29479                 
29480                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29481                 //console.log('adding nested layout panel '  + cfg.toSource());
29482                 this.add(region, ret);
29483                 
29484                 break;
29485                 
29486             case 'GridPanel': 
29487             
29488                 // needs grid and region
29489                 
29490                 //var el = this.getRegion(region).el.createChild();
29491                 var el = this.el.createChild();
29492                 // create the grid first...
29493                 
29494                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29495                 delete cfg.grid;
29496                 if (region == 'center' && this.active ) {
29497                     cfg.background = false;
29498                 }
29499                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29500                 
29501                 this.add(region, ret);
29502                 if (cfg.background) {
29503                     ret.on('activate', function(gp) {
29504                         if (!gp.grid.rendered) {
29505                             gp.grid.render();
29506                         }
29507                     });
29508                 } else {
29509                     grid.render();
29510                 }
29511                 break;
29512            
29513                
29514                 
29515                 
29516             default: 
29517                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29518                 return null;
29519              // GridPanel (grid, cfg)
29520             
29521         }
29522         this.beginUpdate();
29523         // add children..
29524         Roo.each(xitems, function(i)  {
29525             ret.addxtype(i);
29526         });
29527         this.endUpdate();
29528         return ret;
29529         
29530     }
29531 });
29532
29533 /**
29534  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29535  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29536  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29537  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29538  * <pre><code>
29539 // shorthand
29540 var CP = Roo.ContentPanel;
29541
29542 var layout = Roo.BorderLayout.create({
29543     north: {
29544         initialSize: 25,
29545         titlebar: false,
29546         panels: [new CP("north", "North")]
29547     },
29548     west: {
29549         split:true,
29550         initialSize: 200,
29551         minSize: 175,
29552         maxSize: 400,
29553         titlebar: true,
29554         collapsible: true,
29555         panels: [new CP("west", {title: "West"})]
29556     },
29557     east: {
29558         split:true,
29559         initialSize: 202,
29560         minSize: 175,
29561         maxSize: 400,
29562         titlebar: true,
29563         collapsible: true,
29564         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29565     },
29566     south: {
29567         split:true,
29568         initialSize: 100,
29569         minSize: 100,
29570         maxSize: 200,
29571         titlebar: true,
29572         collapsible: true,
29573         panels: [new CP("south", {title: "South", closable: true})]
29574     },
29575     center: {
29576         titlebar: true,
29577         autoScroll:true,
29578         resizeTabs: true,
29579         minTabWidth: 50,
29580         preferredTabWidth: 150,
29581         panels: [
29582             new CP("center1", {title: "Close Me", closable: true}),
29583             new CP("center2", {title: "Center Panel", closable: false})
29584         ]
29585     }
29586 }, document.body);
29587
29588 layout.getRegion("center").showPanel("center1");
29589 </code></pre>
29590  * @param config
29591  * @param targetEl
29592  */
29593 Roo.BorderLayout.create = function(config, targetEl){
29594     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29595     layout.beginUpdate();
29596     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29597     for(var j = 0, jlen = regions.length; j < jlen; j++){
29598         var lr = regions[j];
29599         if(layout.regions[lr] && config[lr].panels){
29600             var r = layout.regions[lr];
29601             var ps = config[lr].panels;
29602             layout.addTypedPanels(r, ps);
29603         }
29604     }
29605     layout.endUpdate();
29606     return layout;
29607 };
29608
29609 // private
29610 Roo.BorderLayout.RegionFactory = {
29611     // private
29612     validRegions : ["north","south","east","west","center"],
29613
29614     // private
29615     create : function(target, mgr, config){
29616         target = target.toLowerCase();
29617         if(config.lightweight || config.basic){
29618             return new Roo.BasicLayoutRegion(mgr, config, target);
29619         }
29620         switch(target){
29621             case "north":
29622                 return new Roo.NorthLayoutRegion(mgr, config);
29623             case "south":
29624                 return new Roo.SouthLayoutRegion(mgr, config);
29625             case "east":
29626                 return new Roo.EastLayoutRegion(mgr, config);
29627             case "west":
29628                 return new Roo.WestLayoutRegion(mgr, config);
29629             case "center":
29630                 return new Roo.CenterLayoutRegion(mgr, config);
29631         }
29632         throw 'Layout region "'+target+'" not supported.';
29633     }
29634 };/*
29635  * Based on:
29636  * Ext JS Library 1.1.1
29637  * Copyright(c) 2006-2007, Ext JS, LLC.
29638  *
29639  * Originally Released Under LGPL - original licence link has changed is not relivant.
29640  *
29641  * Fork - LGPL
29642  * <script type="text/javascript">
29643  */
29644  
29645 /**
29646  * @class Roo.BasicLayoutRegion
29647  * @extends Roo.util.Observable
29648  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29649  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29650  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29651  */
29652 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29653     this.mgr = mgr;
29654     this.position  = pos;
29655     this.events = {
29656         /**
29657          * @scope Roo.BasicLayoutRegion
29658          */
29659         
29660         /**
29661          * @event beforeremove
29662          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29663          * @param {Roo.LayoutRegion} this
29664          * @param {Roo.ContentPanel} panel The panel
29665          * @param {Object} e The cancel event object
29666          */
29667         "beforeremove" : true,
29668         /**
29669          * @event invalidated
29670          * Fires when the layout for this region is changed.
29671          * @param {Roo.LayoutRegion} this
29672          */
29673         "invalidated" : true,
29674         /**
29675          * @event visibilitychange
29676          * Fires when this region is shown or hidden 
29677          * @param {Roo.LayoutRegion} this
29678          * @param {Boolean} visibility true or false
29679          */
29680         "visibilitychange" : true,
29681         /**
29682          * @event paneladded
29683          * Fires when a panel is added. 
29684          * @param {Roo.LayoutRegion} this
29685          * @param {Roo.ContentPanel} panel The panel
29686          */
29687         "paneladded" : true,
29688         /**
29689          * @event panelremoved
29690          * Fires when a panel is removed. 
29691          * @param {Roo.LayoutRegion} this
29692          * @param {Roo.ContentPanel} panel The panel
29693          */
29694         "panelremoved" : true,
29695         /**
29696          * @event collapsed
29697          * Fires when this region is collapsed.
29698          * @param {Roo.LayoutRegion} this
29699          */
29700         "collapsed" : true,
29701         /**
29702          * @event expanded
29703          * Fires when this region is expanded.
29704          * @param {Roo.LayoutRegion} this
29705          */
29706         "expanded" : true,
29707         /**
29708          * @event slideshow
29709          * Fires when this region is slid into view.
29710          * @param {Roo.LayoutRegion} this
29711          */
29712         "slideshow" : true,
29713         /**
29714          * @event slidehide
29715          * Fires when this region slides out of view. 
29716          * @param {Roo.LayoutRegion} this
29717          */
29718         "slidehide" : true,
29719         /**
29720          * @event panelactivated
29721          * Fires when a panel is activated. 
29722          * @param {Roo.LayoutRegion} this
29723          * @param {Roo.ContentPanel} panel The activated panel
29724          */
29725         "panelactivated" : true,
29726         /**
29727          * @event resized
29728          * Fires when the user resizes this region. 
29729          * @param {Roo.LayoutRegion} this
29730          * @param {Number} newSize The new size (width for east/west, height for north/south)
29731          */
29732         "resized" : true
29733     };
29734     /** A collection of panels in this region. @type Roo.util.MixedCollection */
29735     this.panels = new Roo.util.MixedCollection();
29736     this.panels.getKey = this.getPanelId.createDelegate(this);
29737     this.box = null;
29738     this.activePanel = null;
29739     // ensure listeners are added...
29740     
29741     if (config.listeners || config.events) {
29742         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29743             listeners : config.listeners || {},
29744             events : config.events || {}
29745         });
29746     }
29747     
29748     if(skipConfig !== true){
29749         this.applyConfig(config);
29750     }
29751 };
29752
29753 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29754     getPanelId : function(p){
29755         return p.getId();
29756     },
29757     
29758     applyConfig : function(config){
29759         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29760         this.config = config;
29761         
29762     },
29763     
29764     /**
29765      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
29766      * the width, for horizontal (north, south) the height.
29767      * @param {Number} newSize The new width or height
29768      */
29769     resizeTo : function(newSize){
29770         var el = this.el ? this.el :
29771                  (this.activePanel ? this.activePanel.getEl() : null);
29772         if(el){
29773             switch(this.position){
29774                 case "east":
29775                 case "west":
29776                     el.setWidth(newSize);
29777                     this.fireEvent("resized", this, newSize);
29778                 break;
29779                 case "north":
29780                 case "south":
29781                     el.setHeight(newSize);
29782                     this.fireEvent("resized", this, newSize);
29783                 break;                
29784             }
29785         }
29786     },
29787     
29788     getBox : function(){
29789         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29790     },
29791     
29792     getMargins : function(){
29793         return this.margins;
29794     },
29795     
29796     updateBox : function(box){
29797         this.box = box;
29798         var el = this.activePanel.getEl();
29799         el.dom.style.left = box.x + "px";
29800         el.dom.style.top = box.y + "px";
29801         this.activePanel.setSize(box.width, box.height);
29802     },
29803     
29804     /**
29805      * Returns the container element for this region.
29806      * @return {Roo.Element}
29807      */
29808     getEl : function(){
29809         return this.activePanel;
29810     },
29811     
29812     /**
29813      * Returns true if this region is currently visible.
29814      * @return {Boolean}
29815      */
29816     isVisible : function(){
29817         return this.activePanel ? true : false;
29818     },
29819     
29820     setActivePanel : function(panel){
29821         panel = this.getPanel(panel);
29822         if(this.activePanel && this.activePanel != panel){
29823             this.activePanel.setActiveState(false);
29824             this.activePanel.getEl().setLeftTop(-10000,-10000);
29825         }
29826         this.activePanel = panel;
29827         panel.setActiveState(true);
29828         if(this.box){
29829             panel.setSize(this.box.width, this.box.height);
29830         }
29831         this.fireEvent("panelactivated", this, panel);
29832         this.fireEvent("invalidated");
29833     },
29834     
29835     /**
29836      * Show the specified panel.
29837      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29838      * @return {Roo.ContentPanel} The shown panel or null
29839      */
29840     showPanel : function(panel){
29841         if(panel = this.getPanel(panel)){
29842             this.setActivePanel(panel);
29843         }
29844         return panel;
29845     },
29846     
29847     /**
29848      * Get the active panel for this region.
29849      * @return {Roo.ContentPanel} The active panel or null
29850      */
29851     getActivePanel : function(){
29852         return this.activePanel;
29853     },
29854     
29855     /**
29856      * Add the passed ContentPanel(s)
29857      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29858      * @return {Roo.ContentPanel} The panel added (if only one was added)
29859      */
29860     add : function(panel){
29861         if(arguments.length > 1){
29862             for(var i = 0, len = arguments.length; i < len; i++) {
29863                 this.add(arguments[i]);
29864             }
29865             return null;
29866         }
29867         if(this.hasPanel(panel)){
29868             this.showPanel(panel);
29869             return panel;
29870         }
29871         var el = panel.getEl();
29872         if(el.dom.parentNode != this.mgr.el.dom){
29873             this.mgr.el.dom.appendChild(el.dom);
29874         }
29875         if(panel.setRegion){
29876             panel.setRegion(this);
29877         }
29878         this.panels.add(panel);
29879         el.setStyle("position", "absolute");
29880         if(!panel.background){
29881             this.setActivePanel(panel);
29882             if(this.config.initialSize && this.panels.getCount()==1){
29883                 this.resizeTo(this.config.initialSize);
29884             }
29885         }
29886         this.fireEvent("paneladded", this, panel);
29887         return panel;
29888     },
29889     
29890     /**
29891      * Returns true if the panel is in this region.
29892      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29893      * @return {Boolean}
29894      */
29895     hasPanel : function(panel){
29896         if(typeof panel == "object"){ // must be panel obj
29897             panel = panel.getId();
29898         }
29899         return this.getPanel(panel) ? true : false;
29900     },
29901     
29902     /**
29903      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29904      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29905      * @param {Boolean} preservePanel Overrides the config preservePanel option
29906      * @return {Roo.ContentPanel} The panel that was removed
29907      */
29908     remove : function(panel, preservePanel){
29909         panel = this.getPanel(panel);
29910         if(!panel){
29911             return null;
29912         }
29913         var e = {};
29914         this.fireEvent("beforeremove", this, panel, e);
29915         if(e.cancel === true){
29916             return null;
29917         }
29918         var panelId = panel.getId();
29919         this.panels.removeKey(panelId);
29920         return panel;
29921     },
29922     
29923     /**
29924      * Returns the panel specified or null if it's not in this region.
29925      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29926      * @return {Roo.ContentPanel}
29927      */
29928     getPanel : function(id){
29929         if(typeof id == "object"){ // must be panel obj
29930             return id;
29931         }
29932         return this.panels.get(id);
29933     },
29934     
29935     /**
29936      * Returns this regions position (north/south/east/west/center).
29937      * @return {String} 
29938      */
29939     getPosition: function(){
29940         return this.position;    
29941     }
29942 });/*
29943  * Based on:
29944  * Ext JS Library 1.1.1
29945  * Copyright(c) 2006-2007, Ext JS, LLC.
29946  *
29947  * Originally Released Under LGPL - original licence link has changed is not relivant.
29948  *
29949  * Fork - LGPL
29950  * <script type="text/javascript">
29951  */
29952  
29953 /**
29954  * @class Roo.LayoutRegion
29955  * @extends Roo.BasicLayoutRegion
29956  * This class represents a region in a layout manager.
29957  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
29958  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
29959  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
29960  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
29961  * @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})
29962  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
29963  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
29964  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
29965  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
29966  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
29967  * @cfg {String}    title           The title for the region (overrides panel titles)
29968  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
29969  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
29970  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
29971  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
29972  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
29973  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
29974  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
29975  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
29976  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
29977  * @cfg {Boolean}   showPin         True to show a pin button
29978  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
29979  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
29980  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
29981  * @cfg {Number}    width           For East/West panels
29982  * @cfg {Number}    height          For North/South panels
29983  * @cfg {Boolean}   split           To show the splitter
29984  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
29985  */
29986 Roo.LayoutRegion = function(mgr, config, pos){
29987     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
29988     var dh = Roo.DomHelper;
29989     /** This region's container element 
29990     * @type Roo.Element */
29991     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
29992     /** This region's title element 
29993     * @type Roo.Element */
29994
29995     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
29996         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
29997         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
29998     ]}, true);
29999     this.titleEl.enableDisplayMode();
30000     /** This region's title text element 
30001     * @type HTMLElement */
30002     this.titleTextEl = this.titleEl.dom.firstChild;
30003     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
30004     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
30005     this.closeBtn.enableDisplayMode();
30006     this.closeBtn.on("click", this.closeClicked, this);
30007     this.closeBtn.hide();
30008
30009     this.createBody(config);
30010     this.visible = true;
30011     this.collapsed = false;
30012
30013     if(config.hideWhenEmpty){
30014         this.hide();
30015         this.on("paneladded", this.validateVisibility, this);
30016         this.on("panelremoved", this.validateVisibility, this);
30017     }
30018     this.applyConfig(config);
30019 };
30020
30021 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
30022
30023     createBody : function(){
30024         /** This region's body element 
30025         * @type Roo.Element */
30026         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
30027     },
30028
30029     applyConfig : function(c){
30030         if(c.collapsible && this.position != "center" && !this.collapsedEl){
30031             var dh = Roo.DomHelper;
30032             if(c.titlebar !== false){
30033                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
30034                 this.collapseBtn.on("click", this.collapse, this);
30035                 this.collapseBtn.enableDisplayMode();
30036
30037                 if(c.showPin === true || this.showPin){
30038                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
30039                     this.stickBtn.enableDisplayMode();
30040                     this.stickBtn.on("click", this.expand, this);
30041                     this.stickBtn.hide();
30042                 }
30043             }
30044             /** This region's collapsed element
30045             * @type Roo.Element */
30046             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
30047                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
30048             ]}, true);
30049             if(c.floatable !== false){
30050                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
30051                this.collapsedEl.on("click", this.collapseClick, this);
30052             }
30053
30054             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
30055                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
30056                    id: "message", unselectable: "on", style:{"float":"left"}});
30057                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
30058              }
30059             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
30060             this.expandBtn.on("click", this.expand, this);
30061         }
30062         if(this.collapseBtn){
30063             this.collapseBtn.setVisible(c.collapsible == true);
30064         }
30065         this.cmargins = c.cmargins || this.cmargins ||
30066                          (this.position == "west" || this.position == "east" ?
30067                              {top: 0, left: 2, right:2, bottom: 0} :
30068                              {top: 2, left: 0, right:0, bottom: 2});
30069         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30070         this.bottomTabs = c.tabPosition != "top";
30071         this.autoScroll = c.autoScroll || false;
30072         if(this.autoScroll){
30073             this.bodyEl.setStyle("overflow", "auto");
30074         }else{
30075             this.bodyEl.setStyle("overflow", "hidden");
30076         }
30077         //if(c.titlebar !== false){
30078             if((!c.titlebar && !c.title) || c.titlebar === false){
30079                 this.titleEl.hide();
30080             }else{
30081                 this.titleEl.show();
30082                 if(c.title){
30083                     this.titleTextEl.innerHTML = c.title;
30084                 }
30085             }
30086         //}
30087         this.duration = c.duration || .30;
30088         this.slideDuration = c.slideDuration || .45;
30089         this.config = c;
30090         if(c.collapsed){
30091             this.collapse(true);
30092         }
30093         if(c.hidden){
30094             this.hide();
30095         }
30096     },
30097     /**
30098      * Returns true if this region is currently visible.
30099      * @return {Boolean}
30100      */
30101     isVisible : function(){
30102         return this.visible;
30103     },
30104
30105     /**
30106      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
30107      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
30108      */
30109     setCollapsedTitle : function(title){
30110         title = title || "&#160;";
30111         if(this.collapsedTitleTextEl){
30112             this.collapsedTitleTextEl.innerHTML = title;
30113         }
30114     },
30115
30116     getBox : function(){
30117         var b;
30118         if(!this.collapsed){
30119             b = this.el.getBox(false, true);
30120         }else{
30121             b = this.collapsedEl.getBox(false, true);
30122         }
30123         return b;
30124     },
30125
30126     getMargins : function(){
30127         return this.collapsed ? this.cmargins : this.margins;
30128     },
30129
30130     highlight : function(){
30131         this.el.addClass("x-layout-panel-dragover");
30132     },
30133
30134     unhighlight : function(){
30135         this.el.removeClass("x-layout-panel-dragover");
30136     },
30137
30138     updateBox : function(box){
30139         this.box = box;
30140         if(!this.collapsed){
30141             this.el.dom.style.left = box.x + "px";
30142             this.el.dom.style.top = box.y + "px";
30143             this.updateBody(box.width, box.height);
30144         }else{
30145             this.collapsedEl.dom.style.left = box.x + "px";
30146             this.collapsedEl.dom.style.top = box.y + "px";
30147             this.collapsedEl.setSize(box.width, box.height);
30148         }
30149         if(this.tabs){
30150             this.tabs.autoSizeTabs();
30151         }
30152     },
30153
30154     updateBody : function(w, h){
30155         if(w !== null){
30156             this.el.setWidth(w);
30157             w -= this.el.getBorderWidth("rl");
30158             if(this.config.adjustments){
30159                 w += this.config.adjustments[0];
30160             }
30161         }
30162         if(h !== null){
30163             this.el.setHeight(h);
30164             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
30165             h -= this.el.getBorderWidth("tb");
30166             if(this.config.adjustments){
30167                 h += this.config.adjustments[1];
30168             }
30169             this.bodyEl.setHeight(h);
30170             if(this.tabs){
30171                 h = this.tabs.syncHeight(h);
30172             }
30173         }
30174         if(this.panelSize){
30175             w = w !== null ? w : this.panelSize.width;
30176             h = h !== null ? h : this.panelSize.height;
30177         }
30178         if(this.activePanel){
30179             var el = this.activePanel.getEl();
30180             w = w !== null ? w : el.getWidth();
30181             h = h !== null ? h : el.getHeight();
30182             this.panelSize = {width: w, height: h};
30183             this.activePanel.setSize(w, h);
30184         }
30185         if(Roo.isIE && this.tabs){
30186             this.tabs.el.repaint();
30187         }
30188     },
30189
30190     /**
30191      * Returns the container element for this region.
30192      * @return {Roo.Element}
30193      */
30194     getEl : function(){
30195         return this.el;
30196     },
30197
30198     /**
30199      * Hides this region.
30200      */
30201     hide : function(){
30202         if(!this.collapsed){
30203             this.el.dom.style.left = "-2000px";
30204             this.el.hide();
30205         }else{
30206             this.collapsedEl.dom.style.left = "-2000px";
30207             this.collapsedEl.hide();
30208         }
30209         this.visible = false;
30210         this.fireEvent("visibilitychange", this, false);
30211     },
30212
30213     /**
30214      * Shows this region if it was previously hidden.
30215      */
30216     show : function(){
30217         if(!this.collapsed){
30218             this.el.show();
30219         }else{
30220             this.collapsedEl.show();
30221         }
30222         this.visible = true;
30223         this.fireEvent("visibilitychange", this, true);
30224     },
30225
30226     closeClicked : function(){
30227         if(this.activePanel){
30228             this.remove(this.activePanel);
30229         }
30230     },
30231
30232     collapseClick : function(e){
30233         if(this.isSlid){
30234            e.stopPropagation();
30235            this.slideIn();
30236         }else{
30237            e.stopPropagation();
30238            this.slideOut();
30239         }
30240     },
30241
30242     /**
30243      * Collapses this region.
30244      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
30245      */
30246     collapse : function(skipAnim){
30247         if(this.collapsed) return;
30248         this.collapsed = true;
30249         if(this.split){
30250             this.split.el.hide();
30251         }
30252         if(this.config.animate && skipAnim !== true){
30253             this.fireEvent("invalidated", this);
30254             this.animateCollapse();
30255         }else{
30256             this.el.setLocation(-20000,-20000);
30257             this.el.hide();
30258             this.collapsedEl.show();
30259             this.fireEvent("collapsed", this);
30260             this.fireEvent("invalidated", this);
30261         }
30262     },
30263
30264     animateCollapse : function(){
30265         // overridden
30266     },
30267
30268     /**
30269      * Expands this region if it was previously collapsed.
30270      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
30271      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
30272      */
30273     expand : function(e, skipAnim){
30274         if(e) e.stopPropagation();
30275         if(!this.collapsed || this.el.hasActiveFx()) return;
30276         if(this.isSlid){
30277             this.afterSlideIn();
30278             skipAnim = true;
30279         }
30280         this.collapsed = false;
30281         if(this.config.animate && skipAnim !== true){
30282             this.animateExpand();
30283         }else{
30284             this.el.show();
30285             if(this.split){
30286                 this.split.el.show();
30287             }
30288             this.collapsedEl.setLocation(-2000,-2000);
30289             this.collapsedEl.hide();
30290             this.fireEvent("invalidated", this);
30291             this.fireEvent("expanded", this);
30292         }
30293     },
30294
30295     animateExpand : function(){
30296         // overridden
30297     },
30298
30299     initTabs : function()
30300     {
30301         this.bodyEl.setStyle("overflow", "hidden");
30302         var ts = new Roo.TabPanel(
30303                 this.bodyEl.dom,
30304                 {
30305                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
30306                     disableTooltips: this.config.disableTabTips,
30307                     toolbar : this.config.toolbar
30308                 }
30309         );
30310         if(this.config.hideTabs){
30311             ts.stripWrap.setDisplayed(false);
30312         }
30313         this.tabs = ts;
30314         ts.resizeTabs = this.config.resizeTabs === true;
30315         ts.minTabWidth = this.config.minTabWidth || 40;
30316         ts.maxTabWidth = this.config.maxTabWidth || 250;
30317         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
30318         ts.monitorResize = false;
30319         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30320         ts.bodyEl.addClass('x-layout-tabs-body');
30321         this.panels.each(this.initPanelAsTab, this);
30322     },
30323
30324     initPanelAsTab : function(panel){
30325         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
30326                     this.config.closeOnTab && panel.isClosable());
30327         if(panel.tabTip !== undefined){
30328             ti.setTooltip(panel.tabTip);
30329         }
30330         ti.on("activate", function(){
30331               this.setActivePanel(panel);
30332         }, this);
30333         if(this.config.closeOnTab){
30334             ti.on("beforeclose", function(t, e){
30335                 e.cancel = true;
30336                 this.remove(panel);
30337             }, this);
30338         }
30339         return ti;
30340     },
30341
30342     updatePanelTitle : function(panel, title){
30343         if(this.activePanel == panel){
30344             this.updateTitle(title);
30345         }
30346         if(this.tabs){
30347             var ti = this.tabs.getTab(panel.getEl().id);
30348             ti.setText(title);
30349             if(panel.tabTip !== undefined){
30350                 ti.setTooltip(panel.tabTip);
30351             }
30352         }
30353     },
30354
30355     updateTitle : function(title){
30356         if(this.titleTextEl && !this.config.title){
30357             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
30358         }
30359     },
30360
30361     setActivePanel : function(panel){
30362         panel = this.getPanel(panel);
30363         if(this.activePanel && this.activePanel != panel){
30364             this.activePanel.setActiveState(false);
30365         }
30366         this.activePanel = panel;
30367         panel.setActiveState(true);
30368         if(this.panelSize){
30369             panel.setSize(this.panelSize.width, this.panelSize.height);
30370         }
30371         if(this.closeBtn){
30372             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30373         }
30374         this.updateTitle(panel.getTitle());
30375         if(this.tabs){
30376             this.fireEvent("invalidated", this);
30377         }
30378         this.fireEvent("panelactivated", this, panel);
30379     },
30380
30381     /**
30382      * Shows the specified panel.
30383      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30384      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30385      */
30386     showPanel : function(panel){
30387         if(panel = this.getPanel(panel)){
30388             if(this.tabs){
30389                 var tab = this.tabs.getTab(panel.getEl().id);
30390                 if(tab.isHidden()){
30391                     this.tabs.unhideTab(tab.id);
30392                 }
30393                 tab.activate();
30394             }else{
30395                 this.setActivePanel(panel);
30396             }
30397         }
30398         return panel;
30399     },
30400
30401     /**
30402      * Get the active panel for this region.
30403      * @return {Roo.ContentPanel} The active panel or null
30404      */
30405     getActivePanel : function(){
30406         return this.activePanel;
30407     },
30408
30409     validateVisibility : function(){
30410         if(this.panels.getCount() < 1){
30411             this.updateTitle("&#160;");
30412             this.closeBtn.hide();
30413             this.hide();
30414         }else{
30415             if(!this.isVisible()){
30416                 this.show();
30417             }
30418         }
30419     },
30420
30421     /**
30422      * Adds the passed ContentPanel(s) to this region.
30423      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30424      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30425      */
30426     add : function(panel){
30427         if(arguments.length > 1){
30428             for(var i = 0, len = arguments.length; i < len; i++) {
30429                 this.add(arguments[i]);
30430             }
30431             return null;
30432         }
30433         if(this.hasPanel(panel)){
30434             this.showPanel(panel);
30435             return panel;
30436         }
30437         panel.setRegion(this);
30438         this.panels.add(panel);
30439         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30440             this.bodyEl.dom.appendChild(panel.getEl().dom);
30441             if(panel.background !== true){
30442                 this.setActivePanel(panel);
30443             }
30444             this.fireEvent("paneladded", this, panel);
30445             return panel;
30446         }
30447         if(!this.tabs){
30448             this.initTabs();
30449         }else{
30450             this.initPanelAsTab(panel);
30451         }
30452         if(panel.background !== true){
30453             this.tabs.activate(panel.getEl().id);
30454         }
30455         this.fireEvent("paneladded", this, panel);
30456         return panel;
30457     },
30458
30459     /**
30460      * Hides the tab for the specified panel.
30461      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30462      */
30463     hidePanel : function(panel){
30464         if(this.tabs && (panel = this.getPanel(panel))){
30465             this.tabs.hideTab(panel.getEl().id);
30466         }
30467     },
30468
30469     /**
30470      * Unhides the tab for a previously hidden panel.
30471      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30472      */
30473     unhidePanel : function(panel){
30474         if(this.tabs && (panel = this.getPanel(panel))){
30475             this.tabs.unhideTab(panel.getEl().id);
30476         }
30477     },
30478
30479     clearPanels : function(){
30480         while(this.panels.getCount() > 0){
30481              this.remove(this.panels.first());
30482         }
30483     },
30484
30485     /**
30486      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30487      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30488      * @param {Boolean} preservePanel Overrides the config preservePanel option
30489      * @return {Roo.ContentPanel} The panel that was removed
30490      */
30491     remove : function(panel, preservePanel){
30492         panel = this.getPanel(panel);
30493         if(!panel){
30494             return null;
30495         }
30496         var e = {};
30497         this.fireEvent("beforeremove", this, panel, e);
30498         if(e.cancel === true){
30499             return null;
30500         }
30501         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30502         var panelId = panel.getId();
30503         this.panels.removeKey(panelId);
30504         if(preservePanel){
30505             document.body.appendChild(panel.getEl().dom);
30506         }
30507         if(this.tabs){
30508             this.tabs.removeTab(panel.getEl().id);
30509         }else if (!preservePanel){
30510             this.bodyEl.dom.removeChild(panel.getEl().dom);
30511         }
30512         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30513             var p = this.panels.first();
30514             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30515             tempEl.appendChild(p.getEl().dom);
30516             this.bodyEl.update("");
30517             this.bodyEl.dom.appendChild(p.getEl().dom);
30518             tempEl = null;
30519             this.updateTitle(p.getTitle());
30520             this.tabs = null;
30521             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30522             this.setActivePanel(p);
30523         }
30524         panel.setRegion(null);
30525         if(this.activePanel == panel){
30526             this.activePanel = null;
30527         }
30528         if(this.config.autoDestroy !== false && preservePanel !== true){
30529             try{panel.destroy();}catch(e){}
30530         }
30531         this.fireEvent("panelremoved", this, panel);
30532         return panel;
30533     },
30534
30535     /**
30536      * Returns the TabPanel component used by this region
30537      * @return {Roo.TabPanel}
30538      */
30539     getTabs : function(){
30540         return this.tabs;
30541     },
30542
30543     createTool : function(parentEl, className){
30544         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30545             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30546         btn.addClassOnOver("x-layout-tools-button-over");
30547         return btn;
30548     }
30549 });/*
30550  * Based on:
30551  * Ext JS Library 1.1.1
30552  * Copyright(c) 2006-2007, Ext JS, LLC.
30553  *
30554  * Originally Released Under LGPL - original licence link has changed is not relivant.
30555  *
30556  * Fork - LGPL
30557  * <script type="text/javascript">
30558  */
30559  
30560
30561
30562 /**
30563  * @class Roo.SplitLayoutRegion
30564  * @extends Roo.LayoutRegion
30565  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30566  */
30567 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30568     this.cursor = cursor;
30569     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30570 };
30571
30572 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30573     splitTip : "Drag to resize.",
30574     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30575     useSplitTips : false,
30576
30577     applyConfig : function(config){
30578         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30579         if(config.split){
30580             if(!this.split){
30581                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30582                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30583                 /** The SplitBar for this region 
30584                 * @type Roo.SplitBar */
30585                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30586                 this.split.on("moved", this.onSplitMove, this);
30587                 this.split.useShim = config.useShim === true;
30588                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30589                 if(this.useSplitTips){
30590                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30591                 }
30592                 if(config.collapsible){
30593                     this.split.el.on("dblclick", this.collapse,  this);
30594                 }
30595             }
30596             if(typeof config.minSize != "undefined"){
30597                 this.split.minSize = config.minSize;
30598             }
30599             if(typeof config.maxSize != "undefined"){
30600                 this.split.maxSize = config.maxSize;
30601             }
30602             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30603                 this.hideSplitter();
30604             }
30605         }
30606     },
30607
30608     getHMaxSize : function(){
30609          var cmax = this.config.maxSize || 10000;
30610          var center = this.mgr.getRegion("center");
30611          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30612     },
30613
30614     getVMaxSize : function(){
30615          var cmax = this.config.maxSize || 10000;
30616          var center = this.mgr.getRegion("center");
30617          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30618     },
30619
30620     onSplitMove : function(split, newSize){
30621         this.fireEvent("resized", this, newSize);
30622     },
30623     
30624     /** 
30625      * Returns the {@link Roo.SplitBar} for this region.
30626      * @return {Roo.SplitBar}
30627      */
30628     getSplitBar : function(){
30629         return this.split;
30630     },
30631     
30632     hide : function(){
30633         this.hideSplitter();
30634         Roo.SplitLayoutRegion.superclass.hide.call(this);
30635     },
30636
30637     hideSplitter : function(){
30638         if(this.split){
30639             this.split.el.setLocation(-2000,-2000);
30640             this.split.el.hide();
30641         }
30642     },
30643
30644     show : function(){
30645         if(this.split){
30646             this.split.el.show();
30647         }
30648         Roo.SplitLayoutRegion.superclass.show.call(this);
30649     },
30650     
30651     beforeSlide: function(){
30652         if(Roo.isGecko){// firefox overflow auto bug workaround
30653             this.bodyEl.clip();
30654             if(this.tabs) this.tabs.bodyEl.clip();
30655             if(this.activePanel){
30656                 this.activePanel.getEl().clip();
30657                 
30658                 if(this.activePanel.beforeSlide){
30659                     this.activePanel.beforeSlide();
30660                 }
30661             }
30662         }
30663     },
30664     
30665     afterSlide : function(){
30666         if(Roo.isGecko){// firefox overflow auto bug workaround
30667             this.bodyEl.unclip();
30668             if(this.tabs) this.tabs.bodyEl.unclip();
30669             if(this.activePanel){
30670                 this.activePanel.getEl().unclip();
30671                 if(this.activePanel.afterSlide){
30672                     this.activePanel.afterSlide();
30673                 }
30674             }
30675         }
30676     },
30677
30678     initAutoHide : function(){
30679         if(this.autoHide !== false){
30680             if(!this.autoHideHd){
30681                 var st = new Roo.util.DelayedTask(this.slideIn, this);
30682                 this.autoHideHd = {
30683                     "mouseout": function(e){
30684                         if(!e.within(this.el, true)){
30685                             st.delay(500);
30686                         }
30687                     },
30688                     "mouseover" : function(e){
30689                         st.cancel();
30690                     },
30691                     scope : this
30692                 };
30693             }
30694             this.el.on(this.autoHideHd);
30695         }
30696     },
30697
30698     clearAutoHide : function(){
30699         if(this.autoHide !== false){
30700             this.el.un("mouseout", this.autoHideHd.mouseout);
30701             this.el.un("mouseover", this.autoHideHd.mouseover);
30702         }
30703     },
30704
30705     clearMonitor : function(){
30706         Roo.get(document).un("click", this.slideInIf, this);
30707     },
30708
30709     // these names are backwards but not changed for compat
30710     slideOut : function(){
30711         if(this.isSlid || this.el.hasActiveFx()){
30712             return;
30713         }
30714         this.isSlid = true;
30715         if(this.collapseBtn){
30716             this.collapseBtn.hide();
30717         }
30718         this.closeBtnState = this.closeBtn.getStyle('display');
30719         this.closeBtn.hide();
30720         if(this.stickBtn){
30721             this.stickBtn.show();
30722         }
30723         this.el.show();
30724         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30725         this.beforeSlide();
30726         this.el.setStyle("z-index", 10001);
30727         this.el.slideIn(this.getSlideAnchor(), {
30728             callback: function(){
30729                 this.afterSlide();
30730                 this.initAutoHide();
30731                 Roo.get(document).on("click", this.slideInIf, this);
30732                 this.fireEvent("slideshow", this);
30733             },
30734             scope: this,
30735             block: true
30736         });
30737     },
30738
30739     afterSlideIn : function(){
30740         this.clearAutoHide();
30741         this.isSlid = false;
30742         this.clearMonitor();
30743         this.el.setStyle("z-index", "");
30744         if(this.collapseBtn){
30745             this.collapseBtn.show();
30746         }
30747         this.closeBtn.setStyle('display', this.closeBtnState);
30748         if(this.stickBtn){
30749             this.stickBtn.hide();
30750         }
30751         this.fireEvent("slidehide", this);
30752     },
30753
30754     slideIn : function(cb){
30755         if(!this.isSlid || this.el.hasActiveFx()){
30756             Roo.callback(cb);
30757             return;
30758         }
30759         this.isSlid = false;
30760         this.beforeSlide();
30761         this.el.slideOut(this.getSlideAnchor(), {
30762             callback: function(){
30763                 this.el.setLeftTop(-10000, -10000);
30764                 this.afterSlide();
30765                 this.afterSlideIn();
30766                 Roo.callback(cb);
30767             },
30768             scope: this,
30769             block: true
30770         });
30771     },
30772     
30773     slideInIf : function(e){
30774         if(!e.within(this.el)){
30775             this.slideIn();
30776         }
30777     },
30778
30779     animateCollapse : function(){
30780         this.beforeSlide();
30781         this.el.setStyle("z-index", 20000);
30782         var anchor = this.getSlideAnchor();
30783         this.el.slideOut(anchor, {
30784             callback : function(){
30785                 this.el.setStyle("z-index", "");
30786                 this.collapsedEl.slideIn(anchor, {duration:.3});
30787                 this.afterSlide();
30788                 this.el.setLocation(-10000,-10000);
30789                 this.el.hide();
30790                 this.fireEvent("collapsed", this);
30791             },
30792             scope: this,
30793             block: true
30794         });
30795     },
30796
30797     animateExpand : function(){
30798         this.beforeSlide();
30799         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30800         this.el.setStyle("z-index", 20000);
30801         this.collapsedEl.hide({
30802             duration:.1
30803         });
30804         this.el.slideIn(this.getSlideAnchor(), {
30805             callback : function(){
30806                 this.el.setStyle("z-index", "");
30807                 this.afterSlide();
30808                 if(this.split){
30809                     this.split.el.show();
30810                 }
30811                 this.fireEvent("invalidated", this);
30812                 this.fireEvent("expanded", this);
30813             },
30814             scope: this,
30815             block: true
30816         });
30817     },
30818
30819     anchors : {
30820         "west" : "left",
30821         "east" : "right",
30822         "north" : "top",
30823         "south" : "bottom"
30824     },
30825
30826     sanchors : {
30827         "west" : "l",
30828         "east" : "r",
30829         "north" : "t",
30830         "south" : "b"
30831     },
30832
30833     canchors : {
30834         "west" : "tl-tr",
30835         "east" : "tr-tl",
30836         "north" : "tl-bl",
30837         "south" : "bl-tl"
30838     },
30839
30840     getAnchor : function(){
30841         return this.anchors[this.position];
30842     },
30843
30844     getCollapseAnchor : function(){
30845         return this.canchors[this.position];
30846     },
30847
30848     getSlideAnchor : function(){
30849         return this.sanchors[this.position];
30850     },
30851
30852     getAlignAdj : function(){
30853         var cm = this.cmargins;
30854         switch(this.position){
30855             case "west":
30856                 return [0, 0];
30857             break;
30858             case "east":
30859                 return [0, 0];
30860             break;
30861             case "north":
30862                 return [0, 0];
30863             break;
30864             case "south":
30865                 return [0, 0];
30866             break;
30867         }
30868     },
30869
30870     getExpandAdj : function(){
30871         var c = this.collapsedEl, cm = this.cmargins;
30872         switch(this.position){
30873             case "west":
30874                 return [-(cm.right+c.getWidth()+cm.left), 0];
30875             break;
30876             case "east":
30877                 return [cm.right+c.getWidth()+cm.left, 0];
30878             break;
30879             case "north":
30880                 return [0, -(cm.top+cm.bottom+c.getHeight())];
30881             break;
30882             case "south":
30883                 return [0, cm.top+cm.bottom+c.getHeight()];
30884             break;
30885         }
30886     }
30887 });/*
30888  * Based on:
30889  * Ext JS Library 1.1.1
30890  * Copyright(c) 2006-2007, Ext JS, LLC.
30891  *
30892  * Originally Released Under LGPL - original licence link has changed is not relivant.
30893  *
30894  * Fork - LGPL
30895  * <script type="text/javascript">
30896  */
30897 /*
30898  * These classes are private internal classes
30899  */
30900 Roo.CenterLayoutRegion = function(mgr, config){
30901     Roo.LayoutRegion.call(this, mgr, config, "center");
30902     this.visible = true;
30903     this.minWidth = config.minWidth || 20;
30904     this.minHeight = config.minHeight || 20;
30905 };
30906
30907 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
30908     hide : function(){
30909         // center panel can't be hidden
30910     },
30911     
30912     show : function(){
30913         // center panel can't be hidden
30914     },
30915     
30916     getMinWidth: function(){
30917         return this.minWidth;
30918     },
30919     
30920     getMinHeight: function(){
30921         return this.minHeight;
30922     }
30923 });
30924
30925
30926 Roo.NorthLayoutRegion = function(mgr, config){
30927     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
30928     if(this.split){
30929         this.split.placement = Roo.SplitBar.TOP;
30930         this.split.orientation = Roo.SplitBar.VERTICAL;
30931         this.split.el.addClass("x-layout-split-v");
30932     }
30933     var size = config.initialSize || config.height;
30934     if(typeof size != "undefined"){
30935         this.el.setHeight(size);
30936     }
30937 };
30938 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
30939     orientation: Roo.SplitBar.VERTICAL,
30940     getBox : function(){
30941         if(this.collapsed){
30942             return this.collapsedEl.getBox();
30943         }
30944         var box = this.el.getBox();
30945         if(this.split){
30946             box.height += this.split.el.getHeight();
30947         }
30948         return box;
30949     },
30950     
30951     updateBox : function(box){
30952         if(this.split && !this.collapsed){
30953             box.height -= this.split.el.getHeight();
30954             this.split.el.setLeft(box.x);
30955             this.split.el.setTop(box.y+box.height);
30956             this.split.el.setWidth(box.width);
30957         }
30958         if(this.collapsed){
30959             this.updateBody(box.width, null);
30960         }
30961         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30962     }
30963 });
30964
30965 Roo.SouthLayoutRegion = function(mgr, config){
30966     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
30967     if(this.split){
30968         this.split.placement = Roo.SplitBar.BOTTOM;
30969         this.split.orientation = Roo.SplitBar.VERTICAL;
30970         this.split.el.addClass("x-layout-split-v");
30971     }
30972     var size = config.initialSize || config.height;
30973     if(typeof size != "undefined"){
30974         this.el.setHeight(size);
30975     }
30976 };
30977 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
30978     orientation: Roo.SplitBar.VERTICAL,
30979     getBox : function(){
30980         if(this.collapsed){
30981             return this.collapsedEl.getBox();
30982         }
30983         var box = this.el.getBox();
30984         if(this.split){
30985             var sh = this.split.el.getHeight();
30986             box.height += sh;
30987             box.y -= sh;
30988         }
30989         return box;
30990     },
30991     
30992     updateBox : function(box){
30993         if(this.split && !this.collapsed){
30994             var sh = this.split.el.getHeight();
30995             box.height -= sh;
30996             box.y += sh;
30997             this.split.el.setLeft(box.x);
30998             this.split.el.setTop(box.y-sh);
30999             this.split.el.setWidth(box.width);
31000         }
31001         if(this.collapsed){
31002             this.updateBody(box.width, null);
31003         }
31004         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31005     }
31006 });
31007
31008 Roo.EastLayoutRegion = function(mgr, config){
31009     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
31010     if(this.split){
31011         this.split.placement = Roo.SplitBar.RIGHT;
31012         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31013         this.split.el.addClass("x-layout-split-h");
31014     }
31015     var size = config.initialSize || config.width;
31016     if(typeof size != "undefined"){
31017         this.el.setWidth(size);
31018     }
31019 };
31020 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
31021     orientation: Roo.SplitBar.HORIZONTAL,
31022     getBox : function(){
31023         if(this.collapsed){
31024             return this.collapsedEl.getBox();
31025         }
31026         var box = this.el.getBox();
31027         if(this.split){
31028             var sw = this.split.el.getWidth();
31029             box.width += sw;
31030             box.x -= sw;
31031         }
31032         return box;
31033     },
31034
31035     updateBox : function(box){
31036         if(this.split && !this.collapsed){
31037             var sw = this.split.el.getWidth();
31038             box.width -= sw;
31039             this.split.el.setLeft(box.x);
31040             this.split.el.setTop(box.y);
31041             this.split.el.setHeight(box.height);
31042             box.x += sw;
31043         }
31044         if(this.collapsed){
31045             this.updateBody(null, box.height);
31046         }
31047         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31048     }
31049 });
31050
31051 Roo.WestLayoutRegion = function(mgr, config){
31052     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
31053     if(this.split){
31054         this.split.placement = Roo.SplitBar.LEFT;
31055         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31056         this.split.el.addClass("x-layout-split-h");
31057     }
31058     var size = config.initialSize || config.width;
31059     if(typeof size != "undefined"){
31060         this.el.setWidth(size);
31061     }
31062 };
31063 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
31064     orientation: Roo.SplitBar.HORIZONTAL,
31065     getBox : function(){
31066         if(this.collapsed){
31067             return this.collapsedEl.getBox();
31068         }
31069         var box = this.el.getBox();
31070         if(this.split){
31071             box.width += this.split.el.getWidth();
31072         }
31073         return box;
31074     },
31075     
31076     updateBox : function(box){
31077         if(this.split && !this.collapsed){
31078             var sw = this.split.el.getWidth();
31079             box.width -= sw;
31080             this.split.el.setLeft(box.x+box.width);
31081             this.split.el.setTop(box.y);
31082             this.split.el.setHeight(box.height);
31083         }
31084         if(this.collapsed){
31085             this.updateBody(null, box.height);
31086         }
31087         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31088     }
31089 });
31090 /*
31091  * Based on:
31092  * Ext JS Library 1.1.1
31093  * Copyright(c) 2006-2007, Ext JS, LLC.
31094  *
31095  * Originally Released Under LGPL - original licence link has changed is not relivant.
31096  *
31097  * Fork - LGPL
31098  * <script type="text/javascript">
31099  */
31100  
31101  
31102 /*
31103  * Private internal class for reading and applying state
31104  */
31105 Roo.LayoutStateManager = function(layout){
31106      // default empty state
31107      this.state = {
31108         north: {},
31109         south: {},
31110         east: {},
31111         west: {}       
31112     };
31113 };
31114
31115 Roo.LayoutStateManager.prototype = {
31116     init : function(layout, provider){
31117         this.provider = provider;
31118         var state = provider.get(layout.id+"-layout-state");
31119         if(state){
31120             var wasUpdating = layout.isUpdating();
31121             if(!wasUpdating){
31122                 layout.beginUpdate();
31123             }
31124             for(var key in state){
31125                 if(typeof state[key] != "function"){
31126                     var rstate = state[key];
31127                     var r = layout.getRegion(key);
31128                     if(r && rstate){
31129                         if(rstate.size){
31130                             r.resizeTo(rstate.size);
31131                         }
31132                         if(rstate.collapsed == true){
31133                             r.collapse(true);
31134                         }else{
31135                             r.expand(null, true);
31136                         }
31137                     }
31138                 }
31139             }
31140             if(!wasUpdating){
31141                 layout.endUpdate();
31142             }
31143             this.state = state; 
31144         }
31145         this.layout = layout;
31146         layout.on("regionresized", this.onRegionResized, this);
31147         layout.on("regioncollapsed", this.onRegionCollapsed, this);
31148         layout.on("regionexpanded", this.onRegionExpanded, this);
31149     },
31150     
31151     storeState : function(){
31152         this.provider.set(this.layout.id+"-layout-state", this.state);
31153     },
31154     
31155     onRegionResized : function(region, newSize){
31156         this.state[region.getPosition()].size = newSize;
31157         this.storeState();
31158     },
31159     
31160     onRegionCollapsed : function(region){
31161         this.state[region.getPosition()].collapsed = true;
31162         this.storeState();
31163     },
31164     
31165     onRegionExpanded : function(region){
31166         this.state[region.getPosition()].collapsed = false;
31167         this.storeState();
31168     }
31169 };/*
31170  * Based on:
31171  * Ext JS Library 1.1.1
31172  * Copyright(c) 2006-2007, Ext JS, LLC.
31173  *
31174  * Originally Released Under LGPL - original licence link has changed is not relivant.
31175  *
31176  * Fork - LGPL
31177  * <script type="text/javascript">
31178  */
31179 /**
31180  * @class Roo.ContentPanel
31181  * @extends Roo.util.Observable
31182  * A basic ContentPanel element.
31183  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
31184  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
31185  * @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
31186  * @cfg {Boolean}   closable      True if the panel can be closed/removed
31187  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
31188  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
31189  * @cfg {Toolbar}   toolbar       A toolbar for this panel
31190  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
31191  * @cfg {String} title          The title for this panel
31192  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
31193  * @cfg {String} url            Calls {@link #setUrl} with this value
31194  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
31195  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
31196  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
31197  * @cfg {String} content        Raw content to fill content panel with (uses setContent on construction.)
31198
31199  * @constructor
31200  * Create a new ContentPanel.
31201  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
31202  * @param {String/Object} config A string to set only the title or a config object
31203  * @param {String} content (optional) Set the HTML content for this panel
31204  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
31205  */
31206 Roo.ContentPanel = function(el, config, content){
31207     
31208      
31209     /*
31210     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
31211         config = el;
31212         el = Roo.id();
31213     }
31214     if (config && config.parentLayout) { 
31215         el = config.parentLayout.el.createChild(); 
31216     }
31217     */
31218     if(el.autoCreate){ // xtype is available if this is called from factory
31219         config = el;
31220         el = Roo.id();
31221     }
31222     this.el = Roo.get(el);
31223     if(!this.el && config && config.autoCreate){
31224         if(typeof config.autoCreate == "object"){
31225             if(!config.autoCreate.id){
31226                 config.autoCreate.id = config.id||el;
31227             }
31228             this.el = Roo.DomHelper.append(document.body,
31229                         config.autoCreate, true);
31230         }else{
31231             this.el = Roo.DomHelper.append(document.body,
31232                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
31233         }
31234     }
31235     this.closable = false;
31236     this.loaded = false;
31237     this.active = false;
31238     if(typeof config == "string"){
31239         this.title = config;
31240     }else{
31241         Roo.apply(this, config);
31242     }
31243     
31244     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
31245         this.wrapEl = this.el.wrap();    
31246         this.toolbar = new Roo.Toolbar(this.el.insertSibling(false, 'before'), [] , this.toolbar);
31247         
31248     }
31249     
31250     
31251     
31252     if(this.resizeEl){
31253         this.resizeEl = Roo.get(this.resizeEl, true);
31254     }else{
31255         this.resizeEl = this.el;
31256     }
31257     this.addEvents({
31258         /**
31259          * @event activate
31260          * Fires when this panel is activated. 
31261          * @param {Roo.ContentPanel} this
31262          */
31263         "activate" : true,
31264         /**
31265          * @event deactivate
31266          * Fires when this panel is activated. 
31267          * @param {Roo.ContentPanel} this
31268          */
31269         "deactivate" : true,
31270
31271         /**
31272          * @event resize
31273          * Fires when this panel is resized if fitToFrame is true.
31274          * @param {Roo.ContentPanel} this
31275          * @param {Number} width The width after any component adjustments
31276          * @param {Number} height The height after any component adjustments
31277          */
31278         "resize" : true
31279     });
31280     if(this.autoScroll){
31281         this.resizeEl.setStyle("overflow", "auto");
31282     } else {
31283         // fix randome scrolling
31284         this.el.on('scroll', function() {
31285             Roo.log('fix random scolling');
31286             this.scrollTo('top',0); 
31287         });
31288     }
31289     content = content || this.content;
31290     if(content){
31291         this.setContent(content);
31292     }
31293     if(config && config.url){
31294         this.setUrl(this.url, this.params, this.loadOnce);
31295     }
31296     
31297     
31298     
31299     Roo.ContentPanel.superclass.constructor.call(this);
31300 };
31301
31302 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
31303     tabTip:'',
31304     setRegion : function(region){
31305         this.region = region;
31306         if(region){
31307            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
31308         }else{
31309            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
31310         } 
31311     },
31312     
31313     /**
31314      * Returns the toolbar for this Panel if one was configured. 
31315      * @return {Roo.Toolbar} 
31316      */
31317     getToolbar : function(){
31318         return this.toolbar;
31319     },
31320     
31321     setActiveState : function(active){
31322         this.active = active;
31323         if(!active){
31324             this.fireEvent("deactivate", this);
31325         }else{
31326             this.fireEvent("activate", this);
31327         }
31328     },
31329     /**
31330      * Updates this panel's element
31331      * @param {String} content The new content
31332      * @param {Boolean} loadScripts (optional) true to look for and process scripts
31333     */
31334     setContent : function(content, loadScripts){
31335         this.el.update(content, loadScripts);
31336     },
31337
31338     ignoreResize : function(w, h){
31339         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
31340             return true;
31341         }else{
31342             this.lastSize = {width: w, height: h};
31343             return false;
31344         }
31345     },
31346     /**
31347      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
31348      * @return {Roo.UpdateManager} The UpdateManager
31349      */
31350     getUpdateManager : function(){
31351         return this.el.getUpdateManager();
31352     },
31353      /**
31354      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31355      * @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:
31356 <pre><code>
31357 panel.load({
31358     url: "your-url.php",
31359     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31360     callback: yourFunction,
31361     scope: yourObject, //(optional scope)
31362     discardUrl: false,
31363     nocache: false,
31364     text: "Loading...",
31365     timeout: 30,
31366     scripts: false
31367 });
31368 </code></pre>
31369      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31370      * 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.
31371      * @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}
31372      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31373      * @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.
31374      * @return {Roo.ContentPanel} this
31375      */
31376     load : function(){
31377         var um = this.el.getUpdateManager();
31378         um.update.apply(um, arguments);
31379         return this;
31380     },
31381
31382
31383     /**
31384      * 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.
31385      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31386      * @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)
31387      * @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)
31388      * @return {Roo.UpdateManager} The UpdateManager
31389      */
31390     setUrl : function(url, params, loadOnce){
31391         if(this.refreshDelegate){
31392             this.removeListener("activate", this.refreshDelegate);
31393         }
31394         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31395         this.on("activate", this.refreshDelegate);
31396         return this.el.getUpdateManager();
31397     },
31398     
31399     _handleRefresh : function(url, params, loadOnce){
31400         if(!loadOnce || !this.loaded){
31401             var updater = this.el.getUpdateManager();
31402             updater.update(url, params, this._setLoaded.createDelegate(this));
31403         }
31404     },
31405     
31406     _setLoaded : function(){
31407         this.loaded = true;
31408     }, 
31409     
31410     /**
31411      * Returns this panel's id
31412      * @return {String} 
31413      */
31414     getId : function(){
31415         return this.el.id;
31416     },
31417     
31418     /** 
31419      * Returns this panel's element - used by regiosn to add.
31420      * @return {Roo.Element} 
31421      */
31422     getEl : function(){
31423         return this.wrapEl || this.el;
31424     },
31425     
31426     adjustForComponents : function(width, height){
31427         if(this.resizeEl != this.el){
31428             width -= this.el.getFrameWidth('lr');
31429             height -= this.el.getFrameWidth('tb');
31430         }
31431         if(this.toolbar){
31432             var te = this.toolbar.getEl();
31433             height -= te.getHeight();
31434             te.setWidth(width);
31435         }
31436         if(this.adjustments){
31437             width += this.adjustments[0];
31438             height += this.adjustments[1];
31439         }
31440         return {"width": width, "height": height};
31441     },
31442     
31443     setSize : function(width, height){
31444         if(this.fitToFrame && !this.ignoreResize(width, height)){
31445             if(this.fitContainer && this.resizeEl != this.el){
31446                 this.el.setSize(width, height);
31447             }
31448             var size = this.adjustForComponents(width, height);
31449             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31450             this.fireEvent('resize', this, size.width, size.height);
31451         }
31452     },
31453     
31454     /**
31455      * Returns this panel's title
31456      * @return {String} 
31457      */
31458     getTitle : function(){
31459         return this.title;
31460     },
31461     
31462     /**
31463      * Set this panel's title
31464      * @param {String} title
31465      */
31466     setTitle : function(title){
31467         this.title = title;
31468         if(this.region){
31469             this.region.updatePanelTitle(this, title);
31470         }
31471     },
31472     
31473     /**
31474      * Returns true is this panel was configured to be closable
31475      * @return {Boolean} 
31476      */
31477     isClosable : function(){
31478         return this.closable;
31479     },
31480     
31481     beforeSlide : function(){
31482         this.el.clip();
31483         this.resizeEl.clip();
31484     },
31485     
31486     afterSlide : function(){
31487         this.el.unclip();
31488         this.resizeEl.unclip();
31489     },
31490     
31491     /**
31492      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31493      *   Will fail silently if the {@link #setUrl} method has not been called.
31494      *   This does not activate the panel, just updates its content.
31495      */
31496     refresh : function(){
31497         if(this.refreshDelegate){
31498            this.loaded = false;
31499            this.refreshDelegate();
31500         }
31501     },
31502     
31503     /**
31504      * Destroys this panel
31505      */
31506     destroy : function(){
31507         this.el.removeAllListeners();
31508         var tempEl = document.createElement("span");
31509         tempEl.appendChild(this.el.dom);
31510         tempEl.innerHTML = "";
31511         this.el.remove();
31512         this.el = null;
31513     },
31514     
31515     /**
31516      * form - if the content panel contains a form - this is a reference to it.
31517      * @type {Roo.form.Form}
31518      */
31519     form : false,
31520     /**
31521      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
31522      *    This contains a reference to it.
31523      * @type {Roo.View}
31524      */
31525     view : false,
31526     
31527       /**
31528      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31529      * <pre><code>
31530
31531 layout.addxtype({
31532        xtype : 'Form',
31533        items: [ .... ]
31534    }
31535 );
31536
31537 </code></pre>
31538      * @param {Object} cfg Xtype definition of item to add.
31539      */
31540     
31541     addxtype : function(cfg) {
31542         // add form..
31543         if (cfg.xtype.match(/^Form$/)) {
31544             var el = this.el.createChild();
31545
31546             this.form = new  Roo.form.Form(cfg);
31547             
31548             
31549             if ( this.form.allItems.length) this.form.render(el.dom);
31550             return this.form;
31551         }
31552         // should only have one of theses..
31553         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
31554             // views..
31555             cfg.el = this.el.appendChild(document.createElement("div"));
31556             // factory?
31557             var ret = new Roo[cfg.xtype](cfg);
31558             ret.render && ret.render(false, ''); // render blank..
31559             this.view = ret;
31560             return ret;
31561         }
31562         return false;
31563     }
31564 });
31565
31566 /**
31567  * @class Roo.GridPanel
31568  * @extends Roo.ContentPanel
31569  * @constructor
31570  * Create a new GridPanel.
31571  * @param {Roo.grid.Grid} grid The grid for this panel
31572  * @param {String/Object} config A string to set only the panel's title, or a config object
31573  */
31574 Roo.GridPanel = function(grid, config){
31575     
31576   
31577     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31578         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31579         
31580     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31581     
31582     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31583     
31584     if(this.toolbar){
31585         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31586     }
31587     // xtype created footer. - not sure if will work as we normally have to render first..
31588     if (this.footer && !this.footer.el && this.footer.xtype) {
31589         
31590         this.footer.container = this.grid.getView().getFooterPanel(true);
31591         this.footer.dataSource = this.grid.dataSource;
31592         this.footer = Roo.factory(this.footer, Roo);
31593         
31594     }
31595     
31596     grid.monitorWindowResize = false; // turn off autosizing
31597     grid.autoHeight = false;
31598     grid.autoWidth = false;
31599     this.grid = grid;
31600     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31601 };
31602
31603 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31604     getId : function(){
31605         return this.grid.id;
31606     },
31607     
31608     /**
31609      * Returns the grid for this panel
31610      * @return {Roo.grid.Grid} 
31611      */
31612     getGrid : function(){
31613         return this.grid;    
31614     },
31615     
31616     setSize : function(width, height){
31617         if(!this.ignoreResize(width, height)){
31618             var grid = this.grid;
31619             var size = this.adjustForComponents(width, height);
31620             grid.getGridEl().setSize(size.width, size.height);
31621             grid.autoSize();
31622         }
31623     },
31624     
31625     beforeSlide : function(){
31626         this.grid.getView().scroller.clip();
31627     },
31628     
31629     afterSlide : function(){
31630         this.grid.getView().scroller.unclip();
31631     },
31632     
31633     destroy : function(){
31634         this.grid.destroy();
31635         delete this.grid;
31636         Roo.GridPanel.superclass.destroy.call(this); 
31637     }
31638 });
31639
31640
31641 /**
31642  * @class Roo.NestedLayoutPanel
31643  * @extends Roo.ContentPanel
31644  * @constructor
31645  * Create a new NestedLayoutPanel.
31646  * 
31647  * 
31648  * @param {Roo.BorderLayout} layout The layout for this panel
31649  * @param {String/Object} config A string to set only the title or a config object
31650  */
31651 Roo.NestedLayoutPanel = function(layout, config)
31652 {
31653     // construct with only one argument..
31654     /* FIXME - implement nicer consturctors
31655     if (layout.layout) {
31656         config = layout;
31657         layout = config.layout;
31658         delete config.layout;
31659     }
31660     if (layout.xtype && !layout.getEl) {
31661         // then layout needs constructing..
31662         layout = Roo.factory(layout, Roo);
31663     }
31664     */
31665     
31666     
31667     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31668     
31669     layout.monitorWindowResize = false; // turn off autosizing
31670     this.layout = layout;
31671     this.layout.getEl().addClass("x-layout-nested-layout");
31672     
31673     
31674     
31675     
31676 };
31677
31678 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31679
31680     setSize : function(width, height){
31681         if(!this.ignoreResize(width, height)){
31682             var size = this.adjustForComponents(width, height);
31683             var el = this.layout.getEl();
31684             el.setSize(size.width, size.height);
31685             var touch = el.dom.offsetWidth;
31686             this.layout.layout();
31687             // ie requires a double layout on the first pass
31688             if(Roo.isIE && !this.initialized){
31689                 this.initialized = true;
31690                 this.layout.layout();
31691             }
31692         }
31693     },
31694     
31695     // activate all subpanels if not currently active..
31696     
31697     setActiveState : function(active){
31698         this.active = active;
31699         if(!active){
31700             this.fireEvent("deactivate", this);
31701             return;
31702         }
31703         
31704         this.fireEvent("activate", this);
31705         // not sure if this should happen before or after..
31706         if (!this.layout) {
31707             return; // should not happen..
31708         }
31709         var reg = false;
31710         for (var r in this.layout.regions) {
31711             reg = this.layout.getRegion(r);
31712             if (reg.getActivePanel()) {
31713                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
31714                 reg.setActivePanel(reg.getActivePanel());
31715                 continue;
31716             }
31717             if (!reg.panels.length) {
31718                 continue;
31719             }
31720             reg.showPanel(reg.getPanel(0));
31721         }
31722         
31723         
31724         
31725         
31726     },
31727     
31728     /**
31729      * Returns the nested BorderLayout for this panel
31730      * @return {Roo.BorderLayout} 
31731      */
31732     getLayout : function(){
31733         return this.layout;
31734     },
31735     
31736      /**
31737      * Adds a xtype elements to the layout of the nested panel
31738      * <pre><code>
31739
31740 panel.addxtype({
31741        xtype : 'ContentPanel',
31742        region: 'west',
31743        items: [ .... ]
31744    }
31745 );
31746
31747 panel.addxtype({
31748         xtype : 'NestedLayoutPanel',
31749         region: 'west',
31750         layout: {
31751            center: { },
31752            west: { }   
31753         },
31754         items : [ ... list of content panels or nested layout panels.. ]
31755    }
31756 );
31757 </code></pre>
31758      * @param {Object} cfg Xtype definition of item to add.
31759      */
31760     addxtype : function(cfg) {
31761         return this.layout.addxtype(cfg);
31762     
31763     }
31764 });
31765
31766 Roo.ScrollPanel = function(el, config, content){
31767     config = config || {};
31768     config.fitToFrame = true;
31769     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31770     
31771     this.el.dom.style.overflow = "hidden";
31772     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31773     this.el.removeClass("x-layout-inactive-content");
31774     this.el.on("mousewheel", this.onWheel, this);
31775
31776     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
31777     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
31778     up.unselectable(); down.unselectable();
31779     up.on("click", this.scrollUp, this);
31780     down.on("click", this.scrollDown, this);
31781     up.addClassOnOver("x-scroller-btn-over");
31782     down.addClassOnOver("x-scroller-btn-over");
31783     up.addClassOnClick("x-scroller-btn-click");
31784     down.addClassOnClick("x-scroller-btn-click");
31785     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31786
31787     this.resizeEl = this.el;
31788     this.el = wrap; this.up = up; this.down = down;
31789 };
31790
31791 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31792     increment : 100,
31793     wheelIncrement : 5,
31794     scrollUp : function(){
31795         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31796     },
31797
31798     scrollDown : function(){
31799         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31800     },
31801
31802     afterScroll : function(){
31803         var el = this.resizeEl;
31804         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31805         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31806         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31807     },
31808
31809     setSize : function(){
31810         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31811         this.afterScroll();
31812     },
31813
31814     onWheel : function(e){
31815         var d = e.getWheelDelta();
31816         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31817         this.afterScroll();
31818         e.stopEvent();
31819     },
31820
31821     setContent : function(content, loadScripts){
31822         this.resizeEl.update(content, loadScripts);
31823     }
31824
31825 });
31826
31827
31828
31829
31830
31831
31832
31833
31834
31835 /**
31836  * @class Roo.TreePanel
31837  * @extends Roo.ContentPanel
31838  * @constructor
31839  * Create a new TreePanel. - defaults to fit/scoll contents.
31840  * @param {String/Object} config A string to set only the panel's title, or a config object
31841  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
31842  */
31843 Roo.TreePanel = function(config){
31844     var el = config.el;
31845     var tree = config.tree;
31846     delete config.tree; 
31847     delete config.el; // hopefull!
31848     
31849     // wrapper for IE7 strict & safari scroll issue
31850     
31851     var treeEl = el.createChild();
31852     config.resizeEl = treeEl;
31853     
31854     
31855     
31856     Roo.TreePanel.superclass.constructor.call(this, el, config);
31857  
31858  
31859     this.tree = new Roo.tree.TreePanel(treeEl , tree);
31860     //console.log(tree);
31861     this.on('activate', function()
31862     {
31863         if (this.tree.rendered) {
31864             return;
31865         }
31866         //console.log('render tree');
31867         this.tree.render();
31868     });
31869     
31870     this.on('resize',  function (cp, w, h) {
31871             this.tree.innerCt.setWidth(w);
31872             this.tree.innerCt.setHeight(h);
31873             this.tree.innerCt.setStyle('overflow-y', 'auto');
31874     });
31875
31876         
31877     
31878 };
31879
31880 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
31881     fitToFrame : true,
31882     autoScroll : true
31883 });
31884
31885
31886
31887
31888
31889
31890
31891
31892
31893
31894
31895 /*
31896  * Based on:
31897  * Ext JS Library 1.1.1
31898  * Copyright(c) 2006-2007, Ext JS, LLC.
31899  *
31900  * Originally Released Under LGPL - original licence link has changed is not relivant.
31901  *
31902  * Fork - LGPL
31903  * <script type="text/javascript">
31904  */
31905  
31906
31907 /**
31908  * @class Roo.ReaderLayout
31909  * @extends Roo.BorderLayout
31910  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
31911  * center region containing two nested regions (a top one for a list view and one for item preview below),
31912  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
31913  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
31914  * expedites the setup of the overall layout and regions for this common application style.
31915  * Example:
31916  <pre><code>
31917 var reader = new Roo.ReaderLayout();
31918 var CP = Roo.ContentPanel;  // shortcut for adding
31919
31920 reader.beginUpdate();
31921 reader.add("north", new CP("north", "North"));
31922 reader.add("west", new CP("west", {title: "West"}));
31923 reader.add("east", new CP("east", {title: "East"}));
31924
31925 reader.regions.listView.add(new CP("listView", "List"));
31926 reader.regions.preview.add(new CP("preview", "Preview"));
31927 reader.endUpdate();
31928 </code></pre>
31929 * @constructor
31930 * Create a new ReaderLayout
31931 * @param {Object} config Configuration options
31932 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
31933 * document.body if omitted)
31934 */
31935 Roo.ReaderLayout = function(config, renderTo){
31936     var c = config || {size:{}};
31937     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
31938         north: c.north !== false ? Roo.apply({
31939             split:false,
31940             initialSize: 32,
31941             titlebar: false
31942         }, c.north) : false,
31943         west: c.west !== false ? Roo.apply({
31944             split:true,
31945             initialSize: 200,
31946             minSize: 175,
31947             maxSize: 400,
31948             titlebar: true,
31949             collapsible: true,
31950             animate: true,
31951             margins:{left:5,right:0,bottom:5,top:5},
31952             cmargins:{left:5,right:5,bottom:5,top:5}
31953         }, c.west) : false,
31954         east: c.east !== false ? Roo.apply({
31955             split:true,
31956             initialSize: 200,
31957             minSize: 175,
31958             maxSize: 400,
31959             titlebar: true,
31960             collapsible: true,
31961             animate: true,
31962             margins:{left:0,right:5,bottom:5,top:5},
31963             cmargins:{left:5,right:5,bottom:5,top:5}
31964         }, c.east) : false,
31965         center: Roo.apply({
31966             tabPosition: 'top',
31967             autoScroll:false,
31968             closeOnTab: true,
31969             titlebar:false,
31970             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
31971         }, c.center)
31972     });
31973
31974     this.el.addClass('x-reader');
31975
31976     this.beginUpdate();
31977
31978     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
31979         south: c.preview !== false ? Roo.apply({
31980             split:true,
31981             initialSize: 200,
31982             minSize: 100,
31983             autoScroll:true,
31984             collapsible:true,
31985             titlebar: true,
31986             cmargins:{top:5,left:0, right:0, bottom:0}
31987         }, c.preview) : false,
31988         center: Roo.apply({
31989             autoScroll:false,
31990             titlebar:false,
31991             minHeight:200
31992         }, c.listView)
31993     });
31994     this.add('center', new Roo.NestedLayoutPanel(inner,
31995             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
31996
31997     this.endUpdate();
31998
31999     this.regions.preview = inner.getRegion('south');
32000     this.regions.listView = inner.getRegion('center');
32001 };
32002
32003 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
32004  * Based on:
32005  * Ext JS Library 1.1.1
32006  * Copyright(c) 2006-2007, Ext JS, LLC.
32007  *
32008  * Originally Released Under LGPL - original licence link has changed is not relivant.
32009  *
32010  * Fork - LGPL
32011  * <script type="text/javascript">
32012  */
32013  
32014 /**
32015  * @class Roo.grid.Grid
32016  * @extends Roo.util.Observable
32017  * This class represents the primary interface of a component based grid control.
32018  * <br><br>Usage:<pre><code>
32019  var grid = new Roo.grid.Grid("my-container-id", {
32020      ds: myDataStore,
32021      cm: myColModel,
32022      selModel: mySelectionModel,
32023      autoSizeColumns: true,
32024      monitorWindowResize: false,
32025      trackMouseOver: true
32026  });
32027  // set any options
32028  grid.render();
32029  * </code></pre>
32030  * <b>Common Problems:</b><br/>
32031  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
32032  * element will correct this<br/>
32033  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
32034  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
32035  * are unpredictable.<br/>
32036  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
32037  * grid to calculate dimensions/offsets.<br/>
32038   * @constructor
32039  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
32040  * The container MUST have some type of size defined for the grid to fill. The container will be
32041  * automatically set to position relative if it isn't already.
32042  * @param {Object} config A config object that sets properties on this grid.
32043  */
32044 Roo.grid.Grid = function(container, config){
32045         // initialize the container
32046         this.container = Roo.get(container);
32047         this.container.update("");
32048         this.container.setStyle("overflow", "hidden");
32049     this.container.addClass('x-grid-container');
32050
32051     this.id = this.container.id;
32052
32053     Roo.apply(this, config);
32054     // check and correct shorthanded configs
32055     if(this.ds){
32056         this.dataSource = this.ds;
32057         delete this.ds;
32058     }
32059     if(this.cm){
32060         this.colModel = this.cm;
32061         delete this.cm;
32062     }
32063     if(this.sm){
32064         this.selModel = this.sm;
32065         delete this.sm;
32066     }
32067
32068     if (this.selModel) {
32069         this.selModel = Roo.factory(this.selModel, Roo.grid);
32070         this.sm = this.selModel;
32071         this.sm.xmodule = this.xmodule || false;
32072     }
32073     if (typeof(this.colModel.config) == 'undefined') {
32074         this.colModel = new Roo.grid.ColumnModel(this.colModel);
32075         this.cm = this.colModel;
32076         this.cm.xmodule = this.xmodule || false;
32077     }
32078     if (this.dataSource) {
32079         this.dataSource= Roo.factory(this.dataSource, Roo.data);
32080         this.ds = this.dataSource;
32081         this.ds.xmodule = this.xmodule || false;
32082          
32083     }
32084     
32085     
32086     
32087     if(this.width){
32088         this.container.setWidth(this.width);
32089     }
32090
32091     if(this.height){
32092         this.container.setHeight(this.height);
32093     }
32094     /** @private */
32095         this.addEvents({
32096         // raw events
32097         /**
32098          * @event click
32099          * The raw click event for the entire grid.
32100          * @param {Roo.EventObject} e
32101          */
32102         "click" : true,
32103         /**
32104          * @event dblclick
32105          * The raw dblclick event for the entire grid.
32106          * @param {Roo.EventObject} e
32107          */
32108         "dblclick" : true,
32109         /**
32110          * @event contextmenu
32111          * The raw contextmenu event for the entire grid.
32112          * @param {Roo.EventObject} e
32113          */
32114         "contextmenu" : true,
32115         /**
32116          * @event mousedown
32117          * The raw mousedown event for the entire grid.
32118          * @param {Roo.EventObject} e
32119          */
32120         "mousedown" : true,
32121         /**
32122          * @event mouseup
32123          * The raw mouseup event for the entire grid.
32124          * @param {Roo.EventObject} e
32125          */
32126         "mouseup" : true,
32127         /**
32128          * @event mouseover
32129          * The raw mouseover event for the entire grid.
32130          * @param {Roo.EventObject} e
32131          */
32132         "mouseover" : true,
32133         /**
32134          * @event mouseout
32135          * The raw mouseout event for the entire grid.
32136          * @param {Roo.EventObject} e
32137          */
32138         "mouseout" : true,
32139         /**
32140          * @event keypress
32141          * The raw keypress event for the entire grid.
32142          * @param {Roo.EventObject} e
32143          */
32144         "keypress" : true,
32145         /**
32146          * @event keydown
32147          * The raw keydown event for the entire grid.
32148          * @param {Roo.EventObject} e
32149          */
32150         "keydown" : true,
32151
32152         // custom events
32153
32154         /**
32155          * @event cellclick
32156          * Fires when a cell is clicked
32157          * @param {Grid} this
32158          * @param {Number} rowIndex
32159          * @param {Number} columnIndex
32160          * @param {Roo.EventObject} e
32161          */
32162         "cellclick" : true,
32163         /**
32164          * @event celldblclick
32165          * Fires when a cell is double clicked
32166          * @param {Grid} this
32167          * @param {Number} rowIndex
32168          * @param {Number} columnIndex
32169          * @param {Roo.EventObject} e
32170          */
32171         "celldblclick" : true,
32172         /**
32173          * @event rowclick
32174          * Fires when a row is clicked
32175          * @param {Grid} this
32176          * @param {Number} rowIndex
32177          * @param {Roo.EventObject} e
32178          */
32179         "rowclick" : true,
32180         /**
32181          * @event rowdblclick
32182          * Fires when a row is double clicked
32183          * @param {Grid} this
32184          * @param {Number} rowIndex
32185          * @param {Roo.EventObject} e
32186          */
32187         "rowdblclick" : true,
32188         /**
32189          * @event headerclick
32190          * Fires when a header is clicked
32191          * @param {Grid} this
32192          * @param {Number} columnIndex
32193          * @param {Roo.EventObject} e
32194          */
32195         "headerclick" : true,
32196         /**
32197          * @event headerdblclick
32198          * Fires when a header cell is double clicked
32199          * @param {Grid} this
32200          * @param {Number} columnIndex
32201          * @param {Roo.EventObject} e
32202          */
32203         "headerdblclick" : true,
32204         /**
32205          * @event rowcontextmenu
32206          * Fires when a row is right clicked
32207          * @param {Grid} this
32208          * @param {Number} rowIndex
32209          * @param {Roo.EventObject} e
32210          */
32211         "rowcontextmenu" : true,
32212         /**
32213          * @event cellcontextmenu
32214          * Fires when a cell is right clicked
32215          * @param {Grid} this
32216          * @param {Number} rowIndex
32217          * @param {Number} cellIndex
32218          * @param {Roo.EventObject} e
32219          */
32220          "cellcontextmenu" : true,
32221         /**
32222          * @event headercontextmenu
32223          * Fires when a header is right clicked
32224          * @param {Grid} this
32225          * @param {Number} columnIndex
32226          * @param {Roo.EventObject} e
32227          */
32228         "headercontextmenu" : true,
32229         /**
32230          * @event bodyscroll
32231          * Fires when the body element is scrolled
32232          * @param {Number} scrollLeft
32233          * @param {Number} scrollTop
32234          */
32235         "bodyscroll" : true,
32236         /**
32237          * @event columnresize
32238          * Fires when the user resizes a column
32239          * @param {Number} columnIndex
32240          * @param {Number} newSize
32241          */
32242         "columnresize" : true,
32243         /**
32244          * @event columnmove
32245          * Fires when the user moves a column
32246          * @param {Number} oldIndex
32247          * @param {Number} newIndex
32248          */
32249         "columnmove" : true,
32250         /**
32251          * @event startdrag
32252          * Fires when row(s) start being dragged
32253          * @param {Grid} this
32254          * @param {Roo.GridDD} dd The drag drop object
32255          * @param {event} e The raw browser event
32256          */
32257         "startdrag" : true,
32258         /**
32259          * @event enddrag
32260          * Fires when a drag operation is complete
32261          * @param {Grid} this
32262          * @param {Roo.GridDD} dd The drag drop object
32263          * @param {event} e The raw browser event
32264          */
32265         "enddrag" : true,
32266         /**
32267          * @event dragdrop
32268          * Fires when dragged row(s) are dropped on a valid DD target
32269          * @param {Grid} this
32270          * @param {Roo.GridDD} dd The drag drop object
32271          * @param {String} targetId The target drag drop object
32272          * @param {event} e The raw browser event
32273          */
32274         "dragdrop" : true,
32275         /**
32276          * @event dragover
32277          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
32278          * @param {Grid} this
32279          * @param {Roo.GridDD} dd The drag drop object
32280          * @param {String} targetId The target drag drop object
32281          * @param {event} e The raw browser event
32282          */
32283         "dragover" : true,
32284         /**
32285          * @event dragenter
32286          *  Fires when the dragged row(s) first cross another DD target while being dragged
32287          * @param {Grid} this
32288          * @param {Roo.GridDD} dd The drag drop object
32289          * @param {String} targetId The target drag drop object
32290          * @param {event} e The raw browser event
32291          */
32292         "dragenter" : true,
32293         /**
32294          * @event dragout
32295          * Fires when the dragged row(s) leave another DD target while being dragged
32296          * @param {Grid} this
32297          * @param {Roo.GridDD} dd The drag drop object
32298          * @param {String} targetId The target drag drop object
32299          * @param {event} e The raw browser event
32300          */
32301         "dragout" : true,
32302         /**
32303          * @event rowclass
32304          * Fires when a row is rendered, so you can change add a style to it.
32305          * @param {GridView} gridview   The grid view
32306          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
32307          */
32308         'rowclass' : true,
32309
32310         /**
32311          * @event render
32312          * Fires when the grid is rendered
32313          * @param {Grid} grid
32314          */
32315         'render' : true
32316     });
32317
32318     Roo.grid.Grid.superclass.constructor.call(this);
32319 };
32320 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
32321     
32322     /**
32323      * @cfg {String} ddGroup - drag drop group.
32324      */
32325
32326     /**
32327      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32328      */
32329     minColumnWidth : 25,
32330
32331     /**
32332      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32333      * <b>on initial render.</b> It is more efficient to explicitly size the columns
32334      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
32335      */
32336     autoSizeColumns : false,
32337
32338     /**
32339      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32340      */
32341     autoSizeHeaders : true,
32342
32343     /**
32344      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32345      */
32346     monitorWindowResize : true,
32347
32348     /**
32349      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32350      * rows measured to get a columns size. Default is 0 (all rows).
32351      */
32352     maxRowsToMeasure : 0,
32353
32354     /**
32355      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32356      */
32357     trackMouseOver : true,
32358
32359     /**
32360     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
32361     */
32362     
32363     /**
32364     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32365     */
32366     enableDragDrop : false,
32367     
32368     /**
32369     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32370     */
32371     enableColumnMove : true,
32372     
32373     /**
32374     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32375     */
32376     enableColumnHide : true,
32377     
32378     /**
32379     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32380     */
32381     enableRowHeightSync : false,
32382     
32383     /**
32384     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32385     */
32386     stripeRows : true,
32387     
32388     /**
32389     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32390     */
32391     autoHeight : false,
32392
32393     /**
32394      * @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.
32395      */
32396     autoExpandColumn : false,
32397
32398     /**
32399     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32400     * Default is 50.
32401     */
32402     autoExpandMin : 50,
32403
32404     /**
32405     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32406     */
32407     autoExpandMax : 1000,
32408
32409     /**
32410     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32411     */
32412     view : null,
32413
32414     /**
32415     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32416     */
32417     loadMask : false,
32418     /**
32419     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
32420     */
32421     dropTarget: false,
32422     
32423    
32424     
32425     // private
32426     rendered : false,
32427
32428     /**
32429     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32430     * of a fixed width. Default is false.
32431     */
32432     /**
32433     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32434     */
32435     /**
32436      * Called once after all setup has been completed and the grid is ready to be rendered.
32437      * @return {Roo.grid.Grid} this
32438      */
32439     render : function()
32440     {
32441         var c = this.container;
32442         // try to detect autoHeight/width mode
32443         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32444             this.autoHeight = true;
32445         }
32446         var view = this.getView();
32447         view.init(this);
32448
32449         c.on("click", this.onClick, this);
32450         c.on("dblclick", this.onDblClick, this);
32451         c.on("contextmenu", this.onContextMenu, this);
32452         c.on("keydown", this.onKeyDown, this);
32453
32454         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32455
32456         this.getSelectionModel().init(this);
32457
32458         view.render();
32459
32460         if(this.loadMask){
32461             this.loadMask = new Roo.LoadMask(this.container,
32462                     Roo.apply({store:this.dataSource}, this.loadMask));
32463         }
32464         
32465         
32466         if (this.toolbar && this.toolbar.xtype) {
32467             this.toolbar.container = this.getView().getHeaderPanel(true);
32468             this.toolbar = new Roo.Toolbar(this.toolbar);
32469         }
32470         if (this.footer && this.footer.xtype) {
32471             this.footer.dataSource = this.getDataSource();
32472             this.footer.container = this.getView().getFooterPanel(true);
32473             this.footer = Roo.factory(this.footer, Roo);
32474         }
32475         if (this.dropTarget && this.dropTarget.xtype) {
32476             delete this.dropTarget.xtype;
32477             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32478         }
32479         
32480         
32481         this.rendered = true;
32482         this.fireEvent('render', this);
32483         return this;
32484     },
32485
32486         /**
32487          * Reconfigures the grid to use a different Store and Column Model.
32488          * The View will be bound to the new objects and refreshed.
32489          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32490          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32491          */
32492     reconfigure : function(dataSource, colModel){
32493         if(this.loadMask){
32494             this.loadMask.destroy();
32495             this.loadMask = new Roo.LoadMask(this.container,
32496                     Roo.apply({store:dataSource}, this.loadMask));
32497         }
32498         this.view.bind(dataSource, colModel);
32499         this.dataSource = dataSource;
32500         this.colModel = colModel;
32501         this.view.refresh(true);
32502     },
32503
32504     // private
32505     onKeyDown : function(e){
32506         this.fireEvent("keydown", e);
32507     },
32508
32509     /**
32510      * Destroy this grid.
32511      * @param {Boolean} removeEl True to remove the element
32512      */
32513     destroy : function(removeEl, keepListeners){
32514         if(this.loadMask){
32515             this.loadMask.destroy();
32516         }
32517         var c = this.container;
32518         c.removeAllListeners();
32519         this.view.destroy();
32520         this.colModel.purgeListeners();
32521         if(!keepListeners){
32522             this.purgeListeners();
32523         }
32524         c.update("");
32525         if(removeEl === true){
32526             c.remove();
32527         }
32528     },
32529
32530     // private
32531     processEvent : function(name, e){
32532         this.fireEvent(name, e);
32533         var t = e.getTarget();
32534         var v = this.view;
32535         var header = v.findHeaderIndex(t);
32536         if(header !== false){
32537             this.fireEvent("header" + name, this, header, e);
32538         }else{
32539             var row = v.findRowIndex(t);
32540             var cell = v.findCellIndex(t);
32541             if(row !== false){
32542                 this.fireEvent("row" + name, this, row, e);
32543                 if(cell !== false){
32544                     this.fireEvent("cell" + name, this, row, cell, e);
32545                 }
32546             }
32547         }
32548     },
32549
32550     // private
32551     onClick : function(e){
32552         this.processEvent("click", e);
32553     },
32554
32555     // private
32556     onContextMenu : function(e, t){
32557         this.processEvent("contextmenu", e);
32558     },
32559
32560     // private
32561     onDblClick : function(e){
32562         this.processEvent("dblclick", e);
32563     },
32564
32565     // private
32566     walkCells : function(row, col, step, fn, scope){
32567         var cm = this.colModel, clen = cm.getColumnCount();
32568         var ds = this.dataSource, rlen = ds.getCount(), first = true;
32569         if(step < 0){
32570             if(col < 0){
32571                 row--;
32572                 first = false;
32573             }
32574             while(row >= 0){
32575                 if(!first){
32576                     col = clen-1;
32577                 }
32578                 first = false;
32579                 while(col >= 0){
32580                     if(fn.call(scope || this, row, col, cm) === true){
32581                         return [row, col];
32582                     }
32583                     col--;
32584                 }
32585                 row--;
32586             }
32587         } else {
32588             if(col >= clen){
32589                 row++;
32590                 first = false;
32591             }
32592             while(row < rlen){
32593                 if(!first){
32594                     col = 0;
32595                 }
32596                 first = false;
32597                 while(col < clen){
32598                     if(fn.call(scope || this, row, col, cm) === true){
32599                         return [row, col];
32600                     }
32601                     col++;
32602                 }
32603                 row++;
32604             }
32605         }
32606         return null;
32607     },
32608
32609     // private
32610     getSelections : function(){
32611         return this.selModel.getSelections();
32612     },
32613
32614     /**
32615      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32616      * but if manual update is required this method will initiate it.
32617      */
32618     autoSize : function(){
32619         if(this.rendered){
32620             this.view.layout();
32621             if(this.view.adjustForScroll){
32622                 this.view.adjustForScroll();
32623             }
32624         }
32625     },
32626
32627     /**
32628      * Returns the grid's underlying element.
32629      * @return {Element} The element
32630      */
32631     getGridEl : function(){
32632         return this.container;
32633     },
32634
32635     // private for compatibility, overridden by editor grid
32636     stopEditing : function(){},
32637
32638     /**
32639      * Returns the grid's SelectionModel.
32640      * @return {SelectionModel}
32641      */
32642     getSelectionModel : function(){
32643         if(!this.selModel){
32644             this.selModel = new Roo.grid.RowSelectionModel();
32645         }
32646         return this.selModel;
32647     },
32648
32649     /**
32650      * Returns the grid's DataSource.
32651      * @return {DataSource}
32652      */
32653     getDataSource : function(){
32654         return this.dataSource;
32655     },
32656
32657     /**
32658      * Returns the grid's ColumnModel.
32659      * @return {ColumnModel}
32660      */
32661     getColumnModel : function(){
32662         return this.colModel;
32663     },
32664
32665     /**
32666      * Returns the grid's GridView object.
32667      * @return {GridView}
32668      */
32669     getView : function(){
32670         if(!this.view){
32671             this.view = new Roo.grid.GridView(this.viewConfig);
32672         }
32673         return this.view;
32674     },
32675     /**
32676      * Called to get grid's drag proxy text, by default returns this.ddText.
32677      * @return {String}
32678      */
32679     getDragDropText : function(){
32680         var count = this.selModel.getCount();
32681         return String.format(this.ddText, count, count == 1 ? '' : 's');
32682     }
32683 });
32684 /**
32685  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32686  * %0 is replaced with the number of selected rows.
32687  * @type String
32688  */
32689 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
32690  * Based on:
32691  * Ext JS Library 1.1.1
32692  * Copyright(c) 2006-2007, Ext JS, LLC.
32693  *
32694  * Originally Released Under LGPL - original licence link has changed is not relivant.
32695  *
32696  * Fork - LGPL
32697  * <script type="text/javascript">
32698  */
32699  
32700 Roo.grid.AbstractGridView = function(){
32701         this.grid = null;
32702         
32703         this.events = {
32704             "beforerowremoved" : true,
32705             "beforerowsinserted" : true,
32706             "beforerefresh" : true,
32707             "rowremoved" : true,
32708             "rowsinserted" : true,
32709             "rowupdated" : true,
32710             "refresh" : true
32711         };
32712     Roo.grid.AbstractGridView.superclass.constructor.call(this);
32713 };
32714
32715 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32716     rowClass : "x-grid-row",
32717     cellClass : "x-grid-cell",
32718     tdClass : "x-grid-td",
32719     hdClass : "x-grid-hd",
32720     splitClass : "x-grid-hd-split",
32721     
32722         init: function(grid){
32723         this.grid = grid;
32724                 var cid = this.grid.getGridEl().id;
32725         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32726         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32727         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32728         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
32729         },
32730         
32731         getColumnRenderers : function(){
32732         var renderers = [];
32733         var cm = this.grid.colModel;
32734         var colCount = cm.getColumnCount();
32735         for(var i = 0; i < colCount; i++){
32736             renderers[i] = cm.getRenderer(i);
32737         }
32738         return renderers;
32739     },
32740     
32741     getColumnIds : function(){
32742         var ids = [];
32743         var cm = this.grid.colModel;
32744         var colCount = cm.getColumnCount();
32745         for(var i = 0; i < colCount; i++){
32746             ids[i] = cm.getColumnId(i);
32747         }
32748         return ids;
32749     },
32750     
32751     getDataIndexes : function(){
32752         if(!this.indexMap){
32753             this.indexMap = this.buildIndexMap();
32754         }
32755         return this.indexMap.colToData;
32756     },
32757     
32758     getColumnIndexByDataIndex : function(dataIndex){
32759         if(!this.indexMap){
32760             this.indexMap = this.buildIndexMap();
32761         }
32762         return this.indexMap.dataToCol[dataIndex];
32763     },
32764     
32765     /**
32766      * Set a css style for a column dynamically. 
32767      * @param {Number} colIndex The index of the column
32768      * @param {String} name The css property name
32769      * @param {String} value The css value
32770      */
32771     setCSSStyle : function(colIndex, name, value){
32772         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
32773         Roo.util.CSS.updateRule(selector, name, value);
32774     },
32775     
32776     generateRules : function(cm){
32777         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
32778         Roo.util.CSS.removeStyleSheet(rulesId);
32779         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32780             var cid = cm.getColumnId(i);
32781             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
32782                          this.tdSelector, cid, " {\n}\n",
32783                          this.hdSelector, cid, " {\n}\n",
32784                          this.splitSelector, cid, " {\n}\n");
32785         }
32786         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
32787     }
32788 });/*
32789  * Based on:
32790  * Ext JS Library 1.1.1
32791  * Copyright(c) 2006-2007, Ext JS, LLC.
32792  *
32793  * Originally Released Under LGPL - original licence link has changed is not relivant.
32794  *
32795  * Fork - LGPL
32796  * <script type="text/javascript">
32797  */
32798
32799 // private
32800 // This is a support class used internally by the Grid components
32801 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
32802     this.grid = grid;
32803     this.view = grid.getView();
32804     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32805     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
32806     if(hd2){
32807         this.setHandleElId(Roo.id(hd));
32808         this.setOuterHandleElId(Roo.id(hd2));
32809     }
32810     this.scroll = false;
32811 };
32812 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
32813     maxDragWidth: 120,
32814     getDragData : function(e){
32815         var t = Roo.lib.Event.getTarget(e);
32816         var h = this.view.findHeaderCell(t);
32817         if(h){
32818             return {ddel: h.firstChild, header:h};
32819         }
32820         return false;
32821     },
32822
32823     onInitDrag : function(e){
32824         this.view.headersDisabled = true;
32825         var clone = this.dragData.ddel.cloneNode(true);
32826         clone.id = Roo.id();
32827         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
32828         this.proxy.update(clone);
32829         return true;
32830     },
32831
32832     afterValidDrop : function(){
32833         var v = this.view;
32834         setTimeout(function(){
32835             v.headersDisabled = false;
32836         }, 50);
32837     },
32838
32839     afterInvalidDrop : function(){
32840         var v = this.view;
32841         setTimeout(function(){
32842             v.headersDisabled = false;
32843         }, 50);
32844     }
32845 });
32846 /*
32847  * Based on:
32848  * Ext JS Library 1.1.1
32849  * Copyright(c) 2006-2007, Ext JS, LLC.
32850  *
32851  * Originally Released Under LGPL - original licence link has changed is not relivant.
32852  *
32853  * Fork - LGPL
32854  * <script type="text/javascript">
32855  */
32856 // private
32857 // This is a support class used internally by the Grid components
32858 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
32859     this.grid = grid;
32860     this.view = grid.getView();
32861     // split the proxies so they don't interfere with mouse events
32862     this.proxyTop = Roo.DomHelper.append(document.body, {
32863         cls:"col-move-top", html:"&#160;"
32864     }, true);
32865     this.proxyBottom = Roo.DomHelper.append(document.body, {
32866         cls:"col-move-bottom", html:"&#160;"
32867     }, true);
32868     this.proxyTop.hide = this.proxyBottom.hide = function(){
32869         this.setLeftTop(-100,-100);
32870         this.setStyle("visibility", "hidden");
32871     };
32872     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32873     // temporarily disabled
32874     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
32875     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
32876 };
32877 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
32878     proxyOffsets : [-4, -9],
32879     fly: Roo.Element.fly,
32880
32881     getTargetFromEvent : function(e){
32882         var t = Roo.lib.Event.getTarget(e);
32883         var cindex = this.view.findCellIndex(t);
32884         if(cindex !== false){
32885             return this.view.getHeaderCell(cindex);
32886         }
32887         return null;
32888     },
32889
32890     nextVisible : function(h){
32891         var v = this.view, cm = this.grid.colModel;
32892         h = h.nextSibling;
32893         while(h){
32894             if(!cm.isHidden(v.getCellIndex(h))){
32895                 return h;
32896             }
32897             h = h.nextSibling;
32898         }
32899         return null;
32900     },
32901
32902     prevVisible : function(h){
32903         var v = this.view, cm = this.grid.colModel;
32904         h = h.prevSibling;
32905         while(h){
32906             if(!cm.isHidden(v.getCellIndex(h))){
32907                 return h;
32908             }
32909             h = h.prevSibling;
32910         }
32911         return null;
32912     },
32913
32914     positionIndicator : function(h, n, e){
32915         var x = Roo.lib.Event.getPageX(e);
32916         var r = Roo.lib.Dom.getRegion(n.firstChild);
32917         var px, pt, py = r.top + this.proxyOffsets[1];
32918         if((r.right - x) <= (r.right-r.left)/2){
32919             px = r.right+this.view.borderWidth;
32920             pt = "after";
32921         }else{
32922             px = r.left;
32923             pt = "before";
32924         }
32925         var oldIndex = this.view.getCellIndex(h);
32926         var newIndex = this.view.getCellIndex(n);
32927
32928         if(this.grid.colModel.isFixed(newIndex)){
32929             return false;
32930         }
32931
32932         var locked = this.grid.colModel.isLocked(newIndex);
32933
32934         if(pt == "after"){
32935             newIndex++;
32936         }
32937         if(oldIndex < newIndex){
32938             newIndex--;
32939         }
32940         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
32941             return false;
32942         }
32943         px +=  this.proxyOffsets[0];
32944         this.proxyTop.setLeftTop(px, py);
32945         this.proxyTop.show();
32946         if(!this.bottomOffset){
32947             this.bottomOffset = this.view.mainHd.getHeight();
32948         }
32949         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
32950         this.proxyBottom.show();
32951         return pt;
32952     },
32953
32954     onNodeEnter : function(n, dd, e, data){
32955         if(data.header != n){
32956             this.positionIndicator(data.header, n, e);
32957         }
32958     },
32959
32960     onNodeOver : function(n, dd, e, data){
32961         var result = false;
32962         if(data.header != n){
32963             result = this.positionIndicator(data.header, n, e);
32964         }
32965         if(!result){
32966             this.proxyTop.hide();
32967             this.proxyBottom.hide();
32968         }
32969         return result ? this.dropAllowed : this.dropNotAllowed;
32970     },
32971
32972     onNodeOut : function(n, dd, e, data){
32973         this.proxyTop.hide();
32974         this.proxyBottom.hide();
32975     },
32976
32977     onNodeDrop : function(n, dd, e, data){
32978         var h = data.header;
32979         if(h != n){
32980             var cm = this.grid.colModel;
32981             var x = Roo.lib.Event.getPageX(e);
32982             var r = Roo.lib.Dom.getRegion(n.firstChild);
32983             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
32984             var oldIndex = this.view.getCellIndex(h);
32985             var newIndex = this.view.getCellIndex(n);
32986             var locked = cm.isLocked(newIndex);
32987             if(pt == "after"){
32988                 newIndex++;
32989             }
32990             if(oldIndex < newIndex){
32991                 newIndex--;
32992             }
32993             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
32994                 return false;
32995             }
32996             cm.setLocked(oldIndex, locked, true);
32997             cm.moveColumn(oldIndex, newIndex);
32998             this.grid.fireEvent("columnmove", oldIndex, newIndex);
32999             return true;
33000         }
33001         return false;
33002     }
33003 });
33004 /*
33005  * Based on:
33006  * Ext JS Library 1.1.1
33007  * Copyright(c) 2006-2007, Ext JS, LLC.
33008  *
33009  * Originally Released Under LGPL - original licence link has changed is not relivant.
33010  *
33011  * Fork - LGPL
33012  * <script type="text/javascript">
33013  */
33014   
33015 /**
33016  * @class Roo.grid.GridView
33017  * @extends Roo.util.Observable
33018  *
33019  * @constructor
33020  * @param {Object} config
33021  */
33022 Roo.grid.GridView = function(config){
33023     Roo.grid.GridView.superclass.constructor.call(this);
33024     this.el = null;
33025
33026     Roo.apply(this, config);
33027 };
33028
33029 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
33030
33031     /**
33032      * Override this function to apply custom css classes to rows during rendering
33033      * @param {Record} record The record
33034      * @param {Number} index
33035      * @method getRowClass
33036      */
33037     rowClass : "x-grid-row",
33038
33039     cellClass : "x-grid-col",
33040
33041     tdClass : "x-grid-td",
33042
33043     hdClass : "x-grid-hd",
33044
33045     splitClass : "x-grid-split",
33046
33047     sortClasses : ["sort-asc", "sort-desc"],
33048
33049     enableMoveAnim : false,
33050
33051     hlColor: "C3DAF9",
33052
33053     dh : Roo.DomHelper,
33054
33055     fly : Roo.Element.fly,
33056
33057     css : Roo.util.CSS,
33058
33059     borderWidth: 1,
33060
33061     splitOffset: 3,
33062
33063     scrollIncrement : 22,
33064
33065     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
33066
33067     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
33068
33069     bind : function(ds, cm){
33070         if(this.ds){
33071             this.ds.un("load", this.onLoad, this);
33072             this.ds.un("datachanged", this.onDataChange, this);
33073             this.ds.un("add", this.onAdd, this);
33074             this.ds.un("remove", this.onRemove, this);
33075             this.ds.un("update", this.onUpdate, this);
33076             this.ds.un("clear", this.onClear, this);
33077         }
33078         if(ds){
33079             ds.on("load", this.onLoad, this);
33080             ds.on("datachanged", this.onDataChange, this);
33081             ds.on("add", this.onAdd, this);
33082             ds.on("remove", this.onRemove, this);
33083             ds.on("update", this.onUpdate, this);
33084             ds.on("clear", this.onClear, this);
33085         }
33086         this.ds = ds;
33087
33088         if(this.cm){
33089             this.cm.un("widthchange", this.onColWidthChange, this);
33090             this.cm.un("headerchange", this.onHeaderChange, this);
33091             this.cm.un("hiddenchange", this.onHiddenChange, this);
33092             this.cm.un("columnmoved", this.onColumnMove, this);
33093             this.cm.un("columnlockchange", this.onColumnLock, this);
33094         }
33095         if(cm){
33096             this.generateRules(cm);
33097             cm.on("widthchange", this.onColWidthChange, this);
33098             cm.on("headerchange", this.onHeaderChange, this);
33099             cm.on("hiddenchange", this.onHiddenChange, this);
33100             cm.on("columnmoved", this.onColumnMove, this);
33101             cm.on("columnlockchange", this.onColumnLock, this);
33102         }
33103         this.cm = cm;
33104     },
33105
33106     init: function(grid){
33107         Roo.grid.GridView.superclass.init.call(this, grid);
33108
33109         this.bind(grid.dataSource, grid.colModel);
33110
33111         grid.on("headerclick", this.handleHeaderClick, this);
33112
33113         if(grid.trackMouseOver){
33114             grid.on("mouseover", this.onRowOver, this);
33115             grid.on("mouseout", this.onRowOut, this);
33116         }
33117         grid.cancelTextSelection = function(){};
33118         this.gridId = grid.id;
33119
33120         var tpls = this.templates || {};
33121
33122         if(!tpls.master){
33123             tpls.master = new Roo.Template(
33124                '<div class="x-grid" hidefocus="true">',
33125                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
33126                   '<div class="x-grid-topbar"></div>',
33127                   '<div class="x-grid-scroller"><div></div></div>',
33128                   '<div class="x-grid-locked">',
33129                       '<div class="x-grid-header">{lockedHeader}</div>',
33130                       '<div class="x-grid-body">{lockedBody}</div>',
33131                   "</div>",
33132                   '<div class="x-grid-viewport">',
33133                       '<div class="x-grid-header">{header}</div>',
33134                       '<div class="x-grid-body">{body}</div>',
33135                   "</div>",
33136                   '<div class="x-grid-bottombar"></div>',
33137                  
33138                   '<div class="x-grid-resize-proxy">&#160;</div>',
33139                "</div>"
33140             );
33141             tpls.master.disableformats = true;
33142         }
33143
33144         if(!tpls.header){
33145             tpls.header = new Roo.Template(
33146                '<table border="0" cellspacing="0" cellpadding="0">',
33147                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
33148                "</table>{splits}"
33149             );
33150             tpls.header.disableformats = true;
33151         }
33152         tpls.header.compile();
33153
33154         if(!tpls.hcell){
33155             tpls.hcell = new Roo.Template(
33156                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
33157                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
33158                 "</div></td>"
33159              );
33160              tpls.hcell.disableFormats = true;
33161         }
33162         tpls.hcell.compile();
33163
33164         if(!tpls.hsplit){
33165             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
33166             tpls.hsplit.disableFormats = true;
33167         }
33168         tpls.hsplit.compile();
33169
33170         if(!tpls.body){
33171             tpls.body = new Roo.Template(
33172                '<table border="0" cellspacing="0" cellpadding="0">',
33173                "<tbody>{rows}</tbody>",
33174                "</table>"
33175             );
33176             tpls.body.disableFormats = true;
33177         }
33178         tpls.body.compile();
33179
33180         if(!tpls.row){
33181             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
33182             tpls.row.disableFormats = true;
33183         }
33184         tpls.row.compile();
33185
33186         if(!tpls.cell){
33187             tpls.cell = new Roo.Template(
33188                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
33189                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
33190                 "</td>"
33191             );
33192             tpls.cell.disableFormats = true;
33193         }
33194         tpls.cell.compile();
33195
33196         this.templates = tpls;
33197     },
33198
33199     // remap these for backwards compat
33200     onColWidthChange : function(){
33201         this.updateColumns.apply(this, arguments);
33202     },
33203     onHeaderChange : function(){
33204         this.updateHeaders.apply(this, arguments);
33205     }, 
33206     onHiddenChange : function(){
33207         this.handleHiddenChange.apply(this, arguments);
33208     },
33209     onColumnMove : function(){
33210         this.handleColumnMove.apply(this, arguments);
33211     },
33212     onColumnLock : function(){
33213         this.handleLockChange.apply(this, arguments);
33214     },
33215
33216     onDataChange : function(){
33217         this.refresh();
33218         this.updateHeaderSortState();
33219     },
33220
33221     onClear : function(){
33222         this.refresh();
33223     },
33224
33225     onUpdate : function(ds, record){
33226         this.refreshRow(record);
33227     },
33228
33229     refreshRow : function(record){
33230         var ds = this.ds, index;
33231         if(typeof record == 'number'){
33232             index = record;
33233             record = ds.getAt(index);
33234         }else{
33235             index = ds.indexOf(record);
33236         }
33237         this.insertRows(ds, index, index, true);
33238         this.onRemove(ds, record, index+1, true);
33239         this.syncRowHeights(index, index);
33240         this.layout();
33241         this.fireEvent("rowupdated", this, index, record);
33242     },
33243
33244     onAdd : function(ds, records, index){
33245         this.insertRows(ds, index, index + (records.length-1));
33246     },
33247
33248     onRemove : function(ds, record, index, isUpdate){
33249         if(isUpdate !== true){
33250             this.fireEvent("beforerowremoved", this, index, record);
33251         }
33252         var bt = this.getBodyTable(), lt = this.getLockedTable();
33253         if(bt.rows[index]){
33254             bt.firstChild.removeChild(bt.rows[index]);
33255         }
33256         if(lt.rows[index]){
33257             lt.firstChild.removeChild(lt.rows[index]);
33258         }
33259         if(isUpdate !== true){
33260             this.stripeRows(index);
33261             this.syncRowHeights(index, index);
33262             this.layout();
33263             this.fireEvent("rowremoved", this, index, record);
33264         }
33265     },
33266
33267     onLoad : function(){
33268         this.scrollToTop();
33269     },
33270
33271     /**
33272      * Scrolls the grid to the top
33273      */
33274     scrollToTop : function(){
33275         if(this.scroller){
33276             this.scroller.dom.scrollTop = 0;
33277             this.syncScroll();
33278         }
33279     },
33280
33281     /**
33282      * Gets a panel in the header of the grid that can be used for toolbars etc.
33283      * After modifying the contents of this panel a call to grid.autoSize() may be
33284      * required to register any changes in size.
33285      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
33286      * @return Roo.Element
33287      */
33288     getHeaderPanel : function(doShow){
33289         if(doShow){
33290             this.headerPanel.show();
33291         }
33292         return this.headerPanel;
33293     },
33294
33295     /**
33296      * Gets a panel in the footer of the grid that can be used for toolbars etc.
33297      * After modifying the contents of this panel a call to grid.autoSize() may be
33298      * required to register any changes in size.
33299      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
33300      * @return Roo.Element
33301      */
33302     getFooterPanel : function(doShow){
33303         if(doShow){
33304             this.footerPanel.show();
33305         }
33306         return this.footerPanel;
33307     },
33308
33309     initElements : function(){
33310         var E = Roo.Element;
33311         var el = this.grid.getGridEl().dom.firstChild;
33312         var cs = el.childNodes;
33313
33314         this.el = new E(el);
33315         
33316          this.focusEl = new E(el.firstChild);
33317         this.focusEl.swallowEvent("click", true);
33318         
33319         this.headerPanel = new E(cs[1]);
33320         this.headerPanel.enableDisplayMode("block");
33321
33322         this.scroller = new E(cs[2]);
33323         this.scrollSizer = new E(this.scroller.dom.firstChild);
33324
33325         this.lockedWrap = new E(cs[3]);
33326         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33327         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33328
33329         this.mainWrap = new E(cs[4]);
33330         this.mainHd = new E(this.mainWrap.dom.firstChild);
33331         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33332
33333         this.footerPanel = new E(cs[5]);
33334         this.footerPanel.enableDisplayMode("block");
33335
33336         this.resizeProxy = new E(cs[6]);
33337
33338         this.headerSelector = String.format(
33339            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33340            this.lockedHd.id, this.mainHd.id
33341         );
33342
33343         this.splitterSelector = String.format(
33344            '#{0} div.x-grid-split, #{1} div.x-grid-split',
33345            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33346         );
33347     },
33348     idToCssName : function(s)
33349     {
33350         return s.replace(/[^a-z0-9]+/ig, '-');
33351     },
33352
33353     getHeaderCell : function(index){
33354         return Roo.DomQuery.select(this.headerSelector)[index];
33355     },
33356
33357     getHeaderCellMeasure : function(index){
33358         return this.getHeaderCell(index).firstChild;
33359     },
33360
33361     getHeaderCellText : function(index){
33362         return this.getHeaderCell(index).firstChild.firstChild;
33363     },
33364
33365     getLockedTable : function(){
33366         return this.lockedBody.dom.firstChild;
33367     },
33368
33369     getBodyTable : function(){
33370         return this.mainBody.dom.firstChild;
33371     },
33372
33373     getLockedRow : function(index){
33374         return this.getLockedTable().rows[index];
33375     },
33376
33377     getRow : function(index){
33378         return this.getBodyTable().rows[index];
33379     },
33380
33381     getRowComposite : function(index){
33382         if(!this.rowEl){
33383             this.rowEl = new Roo.CompositeElementLite();
33384         }
33385         var els = [], lrow, mrow;
33386         if(lrow = this.getLockedRow(index)){
33387             els.push(lrow);
33388         }
33389         if(mrow = this.getRow(index)){
33390             els.push(mrow);
33391         }
33392         this.rowEl.elements = els;
33393         return this.rowEl;
33394     },
33395
33396     getCell : function(rowIndex, colIndex){
33397         var locked = this.cm.getLockedCount();
33398         var source;
33399         if(colIndex < locked){
33400             source = this.lockedBody.dom.firstChild;
33401         }else{
33402             source = this.mainBody.dom.firstChild;
33403             colIndex -= locked;
33404         }
33405         return source.rows[rowIndex].childNodes[colIndex];
33406     },
33407
33408     getCellText : function(rowIndex, colIndex){
33409         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33410     },
33411
33412     getCellBox : function(cell){
33413         var b = this.fly(cell).getBox();
33414         if(Roo.isOpera){ // opera fails to report the Y
33415             b.y = cell.offsetTop + this.mainBody.getY();
33416         }
33417         return b;
33418     },
33419
33420     getCellIndex : function(cell){
33421         var id = String(cell.className).match(this.cellRE);
33422         if(id){
33423             return parseInt(id[1], 10);
33424         }
33425         return 0;
33426     },
33427
33428     findHeaderIndex : function(n){
33429         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33430         return r ? this.getCellIndex(r) : false;
33431     },
33432
33433     findHeaderCell : function(n){
33434         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33435         return r ? r : false;
33436     },
33437
33438     findRowIndex : function(n){
33439         if(!n){
33440             return false;
33441         }
33442         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33443         return r ? r.rowIndex : false;
33444     },
33445
33446     findCellIndex : function(node){
33447         var stop = this.el.dom;
33448         while(node && node != stop){
33449             if(this.findRE.test(node.className)){
33450                 return this.getCellIndex(node);
33451             }
33452             node = node.parentNode;
33453         }
33454         return false;
33455     },
33456
33457     getColumnId : function(index){
33458         return this.cm.getColumnId(index);
33459     },
33460
33461     getSplitters : function()
33462     {
33463         if(this.splitterSelector){
33464            return Roo.DomQuery.select(this.splitterSelector);
33465         }else{
33466             return null;
33467       }
33468     },
33469
33470     getSplitter : function(index){
33471         return this.getSplitters()[index];
33472     },
33473
33474     onRowOver : function(e, t){
33475         var row;
33476         if((row = this.findRowIndex(t)) !== false){
33477             this.getRowComposite(row).addClass("x-grid-row-over");
33478         }
33479     },
33480
33481     onRowOut : function(e, t){
33482         var row;
33483         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33484             this.getRowComposite(row).removeClass("x-grid-row-over");
33485         }
33486     },
33487
33488     renderHeaders : function(){
33489         var cm = this.cm;
33490         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33491         var cb = [], lb = [], sb = [], lsb = [], p = {};
33492         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33493             p.cellId = "x-grid-hd-0-" + i;
33494             p.splitId = "x-grid-csplit-0-" + i;
33495             p.id = cm.getColumnId(i);
33496             p.title = cm.getColumnTooltip(i) || "";
33497             p.value = cm.getColumnHeader(i) || "";
33498             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33499             if(!cm.isLocked(i)){
33500                 cb[cb.length] = ct.apply(p);
33501                 sb[sb.length] = st.apply(p);
33502             }else{
33503                 lb[lb.length] = ct.apply(p);
33504                 lsb[lsb.length] = st.apply(p);
33505             }
33506         }
33507         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33508                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33509     },
33510
33511     updateHeaders : function(){
33512         var html = this.renderHeaders();
33513         this.lockedHd.update(html[0]);
33514         this.mainHd.update(html[1]);
33515     },
33516
33517     /**
33518      * Focuses the specified row.
33519      * @param {Number} row The row index
33520      */
33521     focusRow : function(row)
33522     {
33523         //Roo.log('GridView.focusRow');
33524         var x = this.scroller.dom.scrollLeft;
33525         this.focusCell(row, 0, false);
33526         this.scroller.dom.scrollLeft = x;
33527     },
33528
33529     /**
33530      * Focuses the specified cell.
33531      * @param {Number} row The row index
33532      * @param {Number} col The column index
33533      * @param {Boolean} hscroll false to disable horizontal scrolling
33534      */
33535     focusCell : function(row, col, hscroll)
33536     {
33537         //Roo.log('GridView.focusCell');
33538         var el = this.ensureVisible(row, col, hscroll);
33539         this.focusEl.alignTo(el, "tl-tl");
33540         if(Roo.isGecko){
33541             this.focusEl.focus();
33542         }else{
33543             this.focusEl.focus.defer(1, this.focusEl);
33544         }
33545     },
33546
33547     /**
33548      * Scrolls the specified cell into view
33549      * @param {Number} row The row index
33550      * @param {Number} col The column index
33551      * @param {Boolean} hscroll false to disable horizontal scrolling
33552      */
33553     ensureVisible : function(row, col, hscroll)
33554     {
33555         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
33556         //return null; //disable for testing.
33557         if(typeof row != "number"){
33558             row = row.rowIndex;
33559         }
33560         if(row < 0 && row >= this.ds.getCount()){
33561             return  null;
33562         }
33563         col = (col !== undefined ? col : 0);
33564         var cm = this.grid.colModel;
33565         while(cm.isHidden(col)){
33566             col++;
33567         }
33568
33569         var el = this.getCell(row, col);
33570         if(!el){
33571             return null;
33572         }
33573         var c = this.scroller.dom;
33574
33575         var ctop = parseInt(el.offsetTop, 10);
33576         var cleft = parseInt(el.offsetLeft, 10);
33577         var cbot = ctop + el.offsetHeight;
33578         var cright = cleft + el.offsetWidth;
33579         
33580         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33581         var stop = parseInt(c.scrollTop, 10);
33582         var sleft = parseInt(c.scrollLeft, 10);
33583         var sbot = stop + ch;
33584         var sright = sleft + c.clientWidth;
33585         /*
33586         Roo.log('GridView.ensureVisible:' +
33587                 ' ctop:' + ctop +
33588                 ' c.clientHeight:' + c.clientHeight +
33589                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
33590                 ' stop:' + stop +
33591                 ' cbot:' + cbot +
33592                 ' sbot:' + sbot +
33593                 ' ch:' + ch  
33594                 );
33595         */
33596         if(ctop < stop){
33597              c.scrollTop = ctop;
33598             //Roo.log("set scrolltop to ctop DISABLE?");
33599         }else if(cbot > sbot){
33600             //Roo.log("set scrolltop to cbot-ch");
33601             c.scrollTop = cbot-ch;
33602         }
33603         
33604         if(hscroll !== false){
33605             if(cleft < sleft){
33606                 c.scrollLeft = cleft;
33607             }else if(cright > sright){
33608                 c.scrollLeft = cright-c.clientWidth;
33609             }
33610         }
33611          
33612         return el;
33613     },
33614
33615     updateColumns : function(){
33616         this.grid.stopEditing();
33617         var cm = this.grid.colModel, colIds = this.getColumnIds();
33618         //var totalWidth = cm.getTotalWidth();
33619         var pos = 0;
33620         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33621             //if(cm.isHidden(i)) continue;
33622             var w = cm.getColumnWidth(i);
33623             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33624             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33625         }
33626         this.updateSplitters();
33627     },
33628
33629     generateRules : function(cm){
33630         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33631         Roo.util.CSS.removeStyleSheet(rulesId);
33632         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33633             var cid = cm.getColumnId(i);
33634             var align = '';
33635             if(cm.config[i].align){
33636                 align = 'text-align:'+cm.config[i].align+';';
33637             }
33638             var hidden = '';
33639             if(cm.isHidden(i)){
33640                 hidden = 'display:none;';
33641             }
33642             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33643             ruleBuf.push(
33644                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33645                     this.hdSelector, cid, " {\n", align, width, "}\n",
33646                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
33647                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
33648         }
33649         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33650     },
33651
33652     updateSplitters : function(){
33653         var cm = this.cm, s = this.getSplitters();
33654         if(s){ // splitters not created yet
33655             var pos = 0, locked = true;
33656             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33657                 if(cm.isHidden(i)) continue;
33658                 var w = cm.getColumnWidth(i); // make sure it's a number
33659                 if(!cm.isLocked(i) && locked){
33660                     pos = 0;
33661                     locked = false;
33662                 }
33663                 pos += w;
33664                 s[i].style.left = (pos-this.splitOffset) + "px";
33665             }
33666         }
33667     },
33668
33669     handleHiddenChange : function(colModel, colIndex, hidden){
33670         if(hidden){
33671             this.hideColumn(colIndex);
33672         }else{
33673             this.unhideColumn(colIndex);
33674         }
33675     },
33676
33677     hideColumn : function(colIndex){
33678         var cid = this.getColumnId(colIndex);
33679         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33680         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33681         if(Roo.isSafari){
33682             this.updateHeaders();
33683         }
33684         this.updateSplitters();
33685         this.layout();
33686     },
33687
33688     unhideColumn : function(colIndex){
33689         var cid = this.getColumnId(colIndex);
33690         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33691         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33692
33693         if(Roo.isSafari){
33694             this.updateHeaders();
33695         }
33696         this.updateSplitters();
33697         this.layout();
33698     },
33699
33700     insertRows : function(dm, firstRow, lastRow, isUpdate){
33701         if(firstRow == 0 && lastRow == dm.getCount()-1){
33702             this.refresh();
33703         }else{
33704             if(!isUpdate){
33705                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33706             }
33707             var s = this.getScrollState();
33708             var markup = this.renderRows(firstRow, lastRow);
33709             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33710             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33711             this.restoreScroll(s);
33712             if(!isUpdate){
33713                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33714                 this.syncRowHeights(firstRow, lastRow);
33715                 this.stripeRows(firstRow);
33716                 this.layout();
33717             }
33718         }
33719     },
33720
33721     bufferRows : function(markup, target, index){
33722         var before = null, trows = target.rows, tbody = target.tBodies[0];
33723         if(index < trows.length){
33724             before = trows[index];
33725         }
33726         var b = document.createElement("div");
33727         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
33728         var rows = b.firstChild.rows;
33729         for(var i = 0, len = rows.length; i < len; i++){
33730             if(before){
33731                 tbody.insertBefore(rows[0], before);
33732             }else{
33733                 tbody.appendChild(rows[0]);
33734             }
33735         }
33736         b.innerHTML = "";
33737         b = null;
33738     },
33739
33740     deleteRows : function(dm, firstRow, lastRow){
33741         if(dm.getRowCount()<1){
33742             this.fireEvent("beforerefresh", this);
33743             this.mainBody.update("");
33744             this.lockedBody.update("");
33745             this.fireEvent("refresh", this);
33746         }else{
33747             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
33748             var bt = this.getBodyTable();
33749             var tbody = bt.firstChild;
33750             var rows = bt.rows;
33751             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
33752                 tbody.removeChild(rows[firstRow]);
33753             }
33754             this.stripeRows(firstRow);
33755             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
33756         }
33757     },
33758
33759     updateRows : function(dataSource, firstRow, lastRow){
33760         var s = this.getScrollState();
33761         this.refresh();
33762         this.restoreScroll(s);
33763     },
33764
33765     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
33766         if(!noRefresh){
33767            this.refresh();
33768         }
33769         this.updateHeaderSortState();
33770     },
33771
33772     getScrollState : function(){
33773         
33774         var sb = this.scroller.dom;
33775         return {left: sb.scrollLeft, top: sb.scrollTop};
33776     },
33777
33778     stripeRows : function(startRow){
33779         if(!this.grid.stripeRows || this.ds.getCount() < 1){
33780             return;
33781         }
33782         startRow = startRow || 0;
33783         var rows = this.getBodyTable().rows;
33784         var lrows = this.getLockedTable().rows;
33785         var cls = ' x-grid-row-alt ';
33786         for(var i = startRow, len = rows.length; i < len; i++){
33787             var row = rows[i], lrow = lrows[i];
33788             var isAlt = ((i+1) % 2 == 0);
33789             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
33790             if(isAlt == hasAlt){
33791                 continue;
33792             }
33793             if(isAlt){
33794                 row.className += " x-grid-row-alt";
33795             }else{
33796                 row.className = row.className.replace("x-grid-row-alt", "");
33797             }
33798             if(lrow){
33799                 lrow.className = row.className;
33800             }
33801         }
33802     },
33803
33804     restoreScroll : function(state){
33805         //Roo.log('GridView.restoreScroll');
33806         var sb = this.scroller.dom;
33807         sb.scrollLeft = state.left;
33808         sb.scrollTop = state.top;
33809         this.syncScroll();
33810     },
33811
33812     syncScroll : function(){
33813         //Roo.log('GridView.syncScroll');
33814         var sb = this.scroller.dom;
33815         var sh = this.mainHd.dom;
33816         var bs = this.mainBody.dom;
33817         var lv = this.lockedBody.dom;
33818         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
33819         lv.scrollTop = bs.scrollTop = sb.scrollTop;
33820     },
33821
33822     handleScroll : function(e){
33823         this.syncScroll();
33824         var sb = this.scroller.dom;
33825         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
33826         e.stopEvent();
33827     },
33828
33829     handleWheel : function(e){
33830         var d = e.getWheelDelta();
33831         this.scroller.dom.scrollTop -= d*22;
33832         // set this here to prevent jumpy scrolling on large tables
33833         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
33834         e.stopEvent();
33835     },
33836
33837     renderRows : function(startRow, endRow){
33838         // pull in all the crap needed to render rows
33839         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
33840         var colCount = cm.getColumnCount();
33841
33842         if(ds.getCount() < 1){
33843             return ["", ""];
33844         }
33845
33846         // build a map for all the columns
33847         var cs = [];
33848         for(var i = 0; i < colCount; i++){
33849             var name = cm.getDataIndex(i);
33850             cs[i] = {
33851                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
33852                 renderer : cm.getRenderer(i),
33853                 id : cm.getColumnId(i),
33854                 locked : cm.isLocked(i)
33855             };
33856         }
33857
33858         startRow = startRow || 0;
33859         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
33860
33861         // records to render
33862         var rs = ds.getRange(startRow, endRow);
33863
33864         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
33865     },
33866
33867     // As much as I hate to duplicate code, this was branched because FireFox really hates
33868     // [].join("") on strings. The performance difference was substantial enough to
33869     // branch this function
33870     doRender : Roo.isGecko ?
33871             function(cs, rs, ds, startRow, colCount, stripe){
33872                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33873                 // buffers
33874                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33875                 
33876                 var hasListener = this.grid.hasListener('rowclass');
33877                 var rowcfg = {};
33878                 for(var j = 0, len = rs.length; j < len; j++){
33879                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
33880                     for(var i = 0; i < colCount; i++){
33881                         c = cs[i];
33882                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33883                         p.id = c.id;
33884                         p.css = p.attr = "";
33885                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33886                         if(p.value == undefined || p.value === "") p.value = "&#160;";
33887                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
33888                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
33889                         }
33890                         var markup = ct.apply(p);
33891                         if(!c.locked){
33892                             cb+= markup;
33893                         }else{
33894                             lcb+= markup;
33895                         }
33896                     }
33897                     var alt = [];
33898                     if(stripe && ((rowIndex+1) % 2 == 0)){
33899                         alt.push("x-grid-row-alt")
33900                     }
33901                     if(r.dirty){
33902                         alt.push(  " x-grid-dirty-row");
33903                     }
33904                     rp.cells = lcb;
33905                     if(this.getRowClass){
33906                         alt.push(this.getRowClass(r, rowIndex));
33907                     }
33908                     if (hasListener) {
33909                         rowcfg = {
33910                              
33911                             record: r,
33912                             rowIndex : rowIndex,
33913                             rowClass : ''
33914                         }
33915                         this.grid.fireEvent('rowclass', this, rowcfg);
33916                         alt.push(rowcfg.rowClass);
33917                     }
33918                     rp.alt = alt.join(" ");
33919                     lbuf+= rt.apply(rp);
33920                     rp.cells = cb;
33921                     buf+=  rt.apply(rp);
33922                 }
33923                 return [lbuf, buf];
33924             } :
33925             function(cs, rs, ds, startRow, colCount, stripe){
33926                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33927                 // buffers
33928                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33929                 var hasListener = this.grid.hasListener('rowclass');
33930                 var rowcfg = {};
33931                 for(var j = 0, len = rs.length; j < len; j++){
33932                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
33933                     for(var i = 0; i < colCount; i++){
33934                         c = cs[i];
33935                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33936                         p.id = c.id;
33937                         p.css = p.attr = "";
33938                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33939                         if(p.value == undefined || p.value === "") p.value = "&#160;";
33940                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
33941                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
33942                         }
33943                         var markup = ct.apply(p);
33944                         if(!c.locked){
33945                             cb[cb.length] = markup;
33946                         }else{
33947                             lcb[lcb.length] = markup;
33948                         }
33949                     }
33950                     var alt = [];
33951                     if(stripe && ((rowIndex+1) % 2 == 0)){
33952                         alt.push( "x-grid-row-alt");
33953                     }
33954                     if(r.dirty){
33955                         alt.push(" x-grid-dirty-row");
33956                     }
33957                     rp.cells = lcb;
33958                     if(this.getRowClass){
33959                         alt.push( this.getRowClass(r, rowIndex));
33960                     }
33961                     if (hasListener) {
33962                         rowcfg = {
33963                              
33964                             record: r,
33965                             rowIndex : rowIndex,
33966                             rowClass : ''
33967                         }
33968                         this.grid.fireEvent('rowclass', this, rowcfg);
33969                         alt.push(rowcfg.rowClass);
33970                     }
33971                     rp.alt = alt.join(" ");
33972                     rp.cells = lcb.join("");
33973                     lbuf[lbuf.length] = rt.apply(rp);
33974                     rp.cells = cb.join("");
33975                     buf[buf.length] =  rt.apply(rp);
33976                 }
33977                 return [lbuf.join(""), buf.join("")];
33978             },
33979
33980     renderBody : function(){
33981         var markup = this.renderRows();
33982         var bt = this.templates.body;
33983         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
33984     },
33985
33986     /**
33987      * Refreshes the grid
33988      * @param {Boolean} headersToo
33989      */
33990     refresh : function(headersToo){
33991         this.fireEvent("beforerefresh", this);
33992         this.grid.stopEditing();
33993         var result = this.renderBody();
33994         this.lockedBody.update(result[0]);
33995         this.mainBody.update(result[1]);
33996         if(headersToo === true){
33997             this.updateHeaders();
33998             this.updateColumns();
33999             this.updateSplitters();
34000             this.updateHeaderSortState();
34001         }
34002         this.syncRowHeights();
34003         this.layout();
34004         this.fireEvent("refresh", this);
34005     },
34006
34007     handleColumnMove : function(cm, oldIndex, newIndex){
34008         this.indexMap = null;
34009         var s = this.getScrollState();
34010         this.refresh(true);
34011         this.restoreScroll(s);
34012         this.afterMove(newIndex);
34013     },
34014
34015     afterMove : function(colIndex){
34016         if(this.enableMoveAnim && Roo.enableFx){
34017             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
34018         }
34019         // if multisort - fix sortOrder, and reload..
34020         if (this.grid.dataSource.multiSort) {
34021             // the we can call sort again..
34022             var dm = this.grid.dataSource;
34023             var cm = this.grid.colModel;
34024             var so = [];
34025             for(var i = 0; i < cm.config.length; i++ ) {
34026                 
34027                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
34028                     continue; // dont' bother, it's not in sort list or being set.
34029                 }
34030                 
34031                 so.push(cm.config[i].dataIndex);
34032             };
34033             dm.sortOrder = so;
34034             dm.load(dm.lastOptions);
34035             
34036             
34037         }
34038         
34039     },
34040
34041     updateCell : function(dm, rowIndex, dataIndex){
34042         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
34043         if(typeof colIndex == "undefined"){ // not present in grid
34044             return;
34045         }
34046         var cm = this.grid.colModel;
34047         var cell = this.getCell(rowIndex, colIndex);
34048         var cellText = this.getCellText(rowIndex, colIndex);
34049
34050         var p = {
34051             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
34052             id : cm.getColumnId(colIndex),
34053             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
34054         };
34055         var renderer = cm.getRenderer(colIndex);
34056         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
34057         if(typeof val == "undefined" || val === "") val = "&#160;";
34058         cellText.innerHTML = val;
34059         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
34060         this.syncRowHeights(rowIndex, rowIndex);
34061     },
34062
34063     calcColumnWidth : function(colIndex, maxRowsToMeasure){
34064         var maxWidth = 0;
34065         if(this.grid.autoSizeHeaders){
34066             var h = this.getHeaderCellMeasure(colIndex);
34067             maxWidth = Math.max(maxWidth, h.scrollWidth);
34068         }
34069         var tb, index;
34070         if(this.cm.isLocked(colIndex)){
34071             tb = this.getLockedTable();
34072             index = colIndex;
34073         }else{
34074             tb = this.getBodyTable();
34075             index = colIndex - this.cm.getLockedCount();
34076         }
34077         if(tb && tb.rows){
34078             var rows = tb.rows;
34079             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
34080             for(var i = 0; i < stopIndex; i++){
34081                 var cell = rows[i].childNodes[index].firstChild;
34082                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
34083             }
34084         }
34085         return maxWidth + /*margin for error in IE*/ 5;
34086     },
34087     /**
34088      * Autofit a column to its content.
34089      * @param {Number} colIndex
34090      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
34091      */
34092      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
34093          if(this.cm.isHidden(colIndex)){
34094              return; // can't calc a hidden column
34095          }
34096         if(forceMinSize){
34097             var cid = this.cm.getColumnId(colIndex);
34098             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
34099            if(this.grid.autoSizeHeaders){
34100                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
34101            }
34102         }
34103         var newWidth = this.calcColumnWidth(colIndex);
34104         this.cm.setColumnWidth(colIndex,
34105             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
34106         if(!suppressEvent){
34107             this.grid.fireEvent("columnresize", colIndex, newWidth);
34108         }
34109     },
34110
34111     /**
34112      * Autofits all columns to their content and then expands to fit any extra space in the grid
34113      */
34114      autoSizeColumns : function(){
34115         var cm = this.grid.colModel;
34116         var colCount = cm.getColumnCount();
34117         for(var i = 0; i < colCount; i++){
34118             this.autoSizeColumn(i, true, true);
34119         }
34120         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
34121             this.fitColumns();
34122         }else{
34123             this.updateColumns();
34124             this.layout();
34125         }
34126     },
34127
34128     /**
34129      * Autofits all columns to the grid's width proportionate with their current size
34130      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
34131      */
34132     fitColumns : function(reserveScrollSpace){
34133         var cm = this.grid.colModel;
34134         var colCount = cm.getColumnCount();
34135         var cols = [];
34136         var width = 0;
34137         var i, w;
34138         for (i = 0; i < colCount; i++){
34139             if(!cm.isHidden(i) && !cm.isFixed(i)){
34140                 w = cm.getColumnWidth(i);
34141                 cols.push(i);
34142                 cols.push(w);
34143                 width += w;
34144             }
34145         }
34146         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
34147         if(reserveScrollSpace){
34148             avail -= 17;
34149         }
34150         var frac = (avail - cm.getTotalWidth())/width;
34151         while (cols.length){
34152             w = cols.pop();
34153             i = cols.pop();
34154             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
34155         }
34156         this.updateColumns();
34157         this.layout();
34158     },
34159
34160     onRowSelect : function(rowIndex){
34161         var row = this.getRowComposite(rowIndex);
34162         row.addClass("x-grid-row-selected");
34163     },
34164
34165     onRowDeselect : function(rowIndex){
34166         var row = this.getRowComposite(rowIndex);
34167         row.removeClass("x-grid-row-selected");
34168     },
34169
34170     onCellSelect : function(row, col){
34171         var cell = this.getCell(row, col);
34172         if(cell){
34173             Roo.fly(cell).addClass("x-grid-cell-selected");
34174         }
34175     },
34176
34177     onCellDeselect : function(row, col){
34178         var cell = this.getCell(row, col);
34179         if(cell){
34180             Roo.fly(cell).removeClass("x-grid-cell-selected");
34181         }
34182     },
34183
34184     updateHeaderSortState : function(){
34185         
34186         // sort state can be single { field: xxx, direction : yyy}
34187         // or   { xxx=>ASC , yyy : DESC ..... }
34188         
34189         var mstate = {};
34190         if (!this.ds.multiSort) { 
34191             var state = this.ds.getSortState();
34192             if(!state){
34193                 return;
34194             }
34195             mstate[state.field] = state.direction;
34196             // FIXME... - this is not used here.. but might be elsewhere..
34197             this.sortState = state;
34198             
34199         } else {
34200             mstate = this.ds.sortToggle;
34201         }
34202         //remove existing sort classes..
34203         
34204         var sc = this.sortClasses;
34205         var hds = this.el.select(this.headerSelector).removeClass(sc);
34206         
34207         for(var f in mstate) {
34208         
34209             var sortColumn = this.cm.findColumnIndex(f);
34210             
34211             if(sortColumn != -1){
34212                 var sortDir = mstate[f];        
34213                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
34214             }
34215         }
34216         
34217          
34218         
34219     },
34220
34221
34222     handleHeaderClick : function(g, index){
34223         if(this.headersDisabled){
34224             return;
34225         }
34226         var dm = g.dataSource, cm = g.colModel;
34227         if(!cm.isSortable(index)){
34228             return;
34229         }
34230         g.stopEditing();
34231         
34232         if (dm.multiSort) {
34233             // update the sortOrder
34234             var so = [];
34235             for(var i = 0; i < cm.config.length; i++ ) {
34236                 
34237                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
34238                     continue; // dont' bother, it's not in sort list or being set.
34239                 }
34240                 
34241                 so.push(cm.config[i].dataIndex);
34242             };
34243             dm.sortOrder = so;
34244         }
34245         
34246         
34247         dm.sort(cm.getDataIndex(index));
34248     },
34249
34250
34251     destroy : function(){
34252         if(this.colMenu){
34253             this.colMenu.removeAll();
34254             Roo.menu.MenuMgr.unregister(this.colMenu);
34255             this.colMenu.getEl().remove();
34256             delete this.colMenu;
34257         }
34258         if(this.hmenu){
34259             this.hmenu.removeAll();
34260             Roo.menu.MenuMgr.unregister(this.hmenu);
34261             this.hmenu.getEl().remove();
34262             delete this.hmenu;
34263         }
34264         if(this.grid.enableColumnMove){
34265             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34266             if(dds){
34267                 for(var dd in dds){
34268                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
34269                         var elid = dds[dd].dragElId;
34270                         dds[dd].unreg();
34271                         Roo.get(elid).remove();
34272                     } else if(dds[dd].config.isTarget){
34273                         dds[dd].proxyTop.remove();
34274                         dds[dd].proxyBottom.remove();
34275                         dds[dd].unreg();
34276                     }
34277                     if(Roo.dd.DDM.locationCache[dd]){
34278                         delete Roo.dd.DDM.locationCache[dd];
34279                     }
34280                 }
34281                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34282             }
34283         }
34284         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
34285         this.bind(null, null);
34286         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
34287     },
34288
34289     handleLockChange : function(){
34290         this.refresh(true);
34291     },
34292
34293     onDenyColumnLock : function(){
34294
34295     },
34296
34297     onDenyColumnHide : function(){
34298
34299     },
34300
34301     handleHdMenuClick : function(item){
34302         var index = this.hdCtxIndex;
34303         var cm = this.cm, ds = this.ds;
34304         switch(item.id){
34305             case "asc":
34306                 ds.sort(cm.getDataIndex(index), "ASC");
34307                 break;
34308             case "desc":
34309                 ds.sort(cm.getDataIndex(index), "DESC");
34310                 break;
34311             case "lock":
34312                 var lc = cm.getLockedCount();
34313                 if(cm.getColumnCount(true) <= lc+1){
34314                     this.onDenyColumnLock();
34315                     return;
34316                 }
34317                 if(lc != index){
34318                     cm.setLocked(index, true, true);
34319                     cm.moveColumn(index, lc);
34320                     this.grid.fireEvent("columnmove", index, lc);
34321                 }else{
34322                     cm.setLocked(index, true);
34323                 }
34324             break;
34325             case "unlock":
34326                 var lc = cm.getLockedCount();
34327                 if((lc-1) != index){
34328                     cm.setLocked(index, false, true);
34329                     cm.moveColumn(index, lc-1);
34330                     this.grid.fireEvent("columnmove", index, lc-1);
34331                 }else{
34332                     cm.setLocked(index, false);
34333                 }
34334             break;
34335             default:
34336                 index = cm.getIndexById(item.id.substr(4));
34337                 if(index != -1){
34338                     if(item.checked && cm.getColumnCount(true) <= 1){
34339                         this.onDenyColumnHide();
34340                         return false;
34341                     }
34342                     cm.setHidden(index, item.checked);
34343                 }
34344         }
34345         return true;
34346     },
34347
34348     beforeColMenuShow : function(){
34349         var cm = this.cm,  colCount = cm.getColumnCount();
34350         this.colMenu.removeAll();
34351         for(var i = 0; i < colCount; i++){
34352             this.colMenu.add(new Roo.menu.CheckItem({
34353                 id: "col-"+cm.getColumnId(i),
34354                 text: cm.getColumnHeader(i),
34355                 checked: !cm.isHidden(i),
34356                 hideOnClick:false
34357             }));
34358         }
34359     },
34360
34361     handleHdCtx : function(g, index, e){
34362         e.stopEvent();
34363         var hd = this.getHeaderCell(index);
34364         this.hdCtxIndex = index;
34365         var ms = this.hmenu.items, cm = this.cm;
34366         ms.get("asc").setDisabled(!cm.isSortable(index));
34367         ms.get("desc").setDisabled(!cm.isSortable(index));
34368         if(this.grid.enableColLock !== false){
34369             ms.get("lock").setDisabled(cm.isLocked(index));
34370             ms.get("unlock").setDisabled(!cm.isLocked(index));
34371         }
34372         this.hmenu.show(hd, "tl-bl");
34373     },
34374
34375     handleHdOver : function(e){
34376         var hd = this.findHeaderCell(e.getTarget());
34377         if(hd && !this.headersDisabled){
34378             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34379                this.fly(hd).addClass("x-grid-hd-over");
34380             }
34381         }
34382     },
34383
34384     handleHdOut : function(e){
34385         var hd = this.findHeaderCell(e.getTarget());
34386         if(hd){
34387             this.fly(hd).removeClass("x-grid-hd-over");
34388         }
34389     },
34390
34391     handleSplitDblClick : function(e, t){
34392         var i = this.getCellIndex(t);
34393         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34394             this.autoSizeColumn(i, true);
34395             this.layout();
34396         }
34397     },
34398
34399     render : function(){
34400
34401         var cm = this.cm;
34402         var colCount = cm.getColumnCount();
34403
34404         if(this.grid.monitorWindowResize === true){
34405             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34406         }
34407         var header = this.renderHeaders();
34408         var body = this.templates.body.apply({rows:""});
34409         var html = this.templates.master.apply({
34410             lockedBody: body,
34411             body: body,
34412             lockedHeader: header[0],
34413             header: header[1]
34414         });
34415
34416         //this.updateColumns();
34417
34418         this.grid.getGridEl().dom.innerHTML = html;
34419
34420         this.initElements();
34421         
34422         // a kludge to fix the random scolling effect in webkit
34423         this.el.on("scroll", function() {
34424             this.el.dom.scrollTop=0; // hopefully not recursive..
34425         },this);
34426
34427         this.scroller.on("scroll", this.handleScroll, this);
34428         this.lockedBody.on("mousewheel", this.handleWheel, this);
34429         this.mainBody.on("mousewheel", this.handleWheel, this);
34430
34431         this.mainHd.on("mouseover", this.handleHdOver, this);
34432         this.mainHd.on("mouseout", this.handleHdOut, this);
34433         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34434                 {delegate: "."+this.splitClass});
34435
34436         this.lockedHd.on("mouseover", this.handleHdOver, this);
34437         this.lockedHd.on("mouseout", this.handleHdOut, this);
34438         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34439                 {delegate: "."+this.splitClass});
34440
34441         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34442             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34443         }
34444
34445         this.updateSplitters();
34446
34447         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34448             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34449             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34450         }
34451
34452         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34453             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34454             this.hmenu.add(
34455                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34456                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34457             );
34458             if(this.grid.enableColLock !== false){
34459                 this.hmenu.add('-',
34460                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34461                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34462                 );
34463             }
34464             if(this.grid.enableColumnHide !== false){
34465
34466                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34467                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34468                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34469
34470                 this.hmenu.add('-',
34471                     {id:"columns", text: this.columnsText, menu: this.colMenu}
34472                 );
34473             }
34474             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34475
34476             this.grid.on("headercontextmenu", this.handleHdCtx, this);
34477         }
34478
34479         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34480             this.dd = new Roo.grid.GridDragZone(this.grid, {
34481                 ddGroup : this.grid.ddGroup || 'GridDD'
34482             });
34483         }
34484
34485         /*
34486         for(var i = 0; i < colCount; i++){
34487             if(cm.isHidden(i)){
34488                 this.hideColumn(i);
34489             }
34490             if(cm.config[i].align){
34491                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34492                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34493             }
34494         }*/
34495         
34496         this.updateHeaderSortState();
34497
34498         this.beforeInitialResize();
34499         this.layout(true);
34500
34501         // two part rendering gives faster view to the user
34502         this.renderPhase2.defer(1, this);
34503     },
34504
34505     renderPhase2 : function(){
34506         // render the rows now
34507         this.refresh();
34508         if(this.grid.autoSizeColumns){
34509             this.autoSizeColumns();
34510         }
34511     },
34512
34513     beforeInitialResize : function(){
34514
34515     },
34516
34517     onColumnSplitterMoved : function(i, w){
34518         this.userResized = true;
34519         var cm = this.grid.colModel;
34520         cm.setColumnWidth(i, w, true);
34521         var cid = cm.getColumnId(i);
34522         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34523         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34524         this.updateSplitters();
34525         this.layout();
34526         this.grid.fireEvent("columnresize", i, w);
34527     },
34528
34529     syncRowHeights : function(startIndex, endIndex){
34530         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34531             startIndex = startIndex || 0;
34532             var mrows = this.getBodyTable().rows;
34533             var lrows = this.getLockedTable().rows;
34534             var len = mrows.length-1;
34535             endIndex = Math.min(endIndex || len, len);
34536             for(var i = startIndex; i <= endIndex; i++){
34537                 var m = mrows[i], l = lrows[i];
34538                 var h = Math.max(m.offsetHeight, l.offsetHeight);
34539                 m.style.height = l.style.height = h + "px";
34540             }
34541         }
34542     },
34543
34544     layout : function(initialRender, is2ndPass){
34545         var g = this.grid;
34546         var auto = g.autoHeight;
34547         var scrollOffset = 16;
34548         var c = g.getGridEl(), cm = this.cm,
34549                 expandCol = g.autoExpandColumn,
34550                 gv = this;
34551         //c.beginMeasure();
34552
34553         if(!c.dom.offsetWidth){ // display:none?
34554             if(initialRender){
34555                 this.lockedWrap.show();
34556                 this.mainWrap.show();
34557             }
34558             return;
34559         }
34560
34561         var hasLock = this.cm.isLocked(0);
34562
34563         var tbh = this.headerPanel.getHeight();
34564         var bbh = this.footerPanel.getHeight();
34565
34566         if(auto){
34567             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34568             var newHeight = ch + c.getBorderWidth("tb");
34569             if(g.maxHeight){
34570                 newHeight = Math.min(g.maxHeight, newHeight);
34571             }
34572             c.setHeight(newHeight);
34573         }
34574
34575         if(g.autoWidth){
34576             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34577         }
34578
34579         var s = this.scroller;
34580
34581         var csize = c.getSize(true);
34582
34583         this.el.setSize(csize.width, csize.height);
34584
34585         this.headerPanel.setWidth(csize.width);
34586         this.footerPanel.setWidth(csize.width);
34587
34588         var hdHeight = this.mainHd.getHeight();
34589         var vw = csize.width;
34590         var vh = csize.height - (tbh + bbh);
34591
34592         s.setSize(vw, vh);
34593
34594         var bt = this.getBodyTable();
34595         var ltWidth = hasLock ?
34596                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34597
34598         var scrollHeight = bt.offsetHeight;
34599         var scrollWidth = ltWidth + bt.offsetWidth;
34600         var vscroll = false, hscroll = false;
34601
34602         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34603
34604         var lw = this.lockedWrap, mw = this.mainWrap;
34605         var lb = this.lockedBody, mb = this.mainBody;
34606
34607         setTimeout(function(){
34608             var t = s.dom.offsetTop;
34609             var w = s.dom.clientWidth,
34610                 h = s.dom.clientHeight;
34611
34612             lw.setTop(t);
34613             lw.setSize(ltWidth, h);
34614
34615             mw.setLeftTop(ltWidth, t);
34616             mw.setSize(w-ltWidth, h);
34617
34618             lb.setHeight(h-hdHeight);
34619             mb.setHeight(h-hdHeight);
34620
34621             if(is2ndPass !== true && !gv.userResized && expandCol){
34622                 // high speed resize without full column calculation
34623                 
34624                 var ci = cm.getIndexById(expandCol);
34625                 if (ci < 0) {
34626                     ci = cm.findColumnIndex(expandCol);
34627                 }
34628                 ci = Math.max(0, ci); // make sure it's got at least the first col.
34629                 var expandId = cm.getColumnId(ci);
34630                 var  tw = cm.getTotalWidth(false);
34631                 var currentWidth = cm.getColumnWidth(ci);
34632                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34633                 if(currentWidth != cw){
34634                     cm.setColumnWidth(ci, cw, true);
34635                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34636                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34637                     gv.updateSplitters();
34638                     gv.layout(false, true);
34639                 }
34640             }
34641
34642             if(initialRender){
34643                 lw.show();
34644                 mw.show();
34645             }
34646             //c.endMeasure();
34647         }, 10);
34648     },
34649
34650     onWindowResize : function(){
34651         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34652             return;
34653         }
34654         this.layout();
34655     },
34656
34657     appendFooter : function(parentEl){
34658         return null;
34659     },
34660
34661     sortAscText : "Sort Ascending",
34662     sortDescText : "Sort Descending",
34663     lockText : "Lock Column",
34664     unlockText : "Unlock Column",
34665     columnsText : "Columns"
34666 });
34667
34668
34669 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
34670     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
34671     this.proxy.el.addClass('x-grid3-col-dd');
34672 };
34673
34674 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
34675     handleMouseDown : function(e){
34676
34677     },
34678
34679     callHandleMouseDown : function(e){
34680         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
34681     }
34682 });
34683 /*
34684  * Based on:
34685  * Ext JS Library 1.1.1
34686  * Copyright(c) 2006-2007, Ext JS, LLC.
34687  *
34688  * Originally Released Under LGPL - original licence link has changed is not relivant.
34689  *
34690  * Fork - LGPL
34691  * <script type="text/javascript">
34692  */
34693  
34694 // private
34695 // This is a support class used internally by the Grid components
34696 Roo.grid.SplitDragZone = function(grid, hd, hd2){
34697     this.grid = grid;
34698     this.view = grid.getView();
34699     this.proxy = this.view.resizeProxy;
34700     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
34701         "gridSplitters" + this.grid.getGridEl().id, {
34702         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
34703     });
34704     this.setHandleElId(Roo.id(hd));
34705     this.setOuterHandleElId(Roo.id(hd2));
34706     this.scroll = false;
34707 };
34708 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
34709     fly: Roo.Element.fly,
34710
34711     b4StartDrag : function(x, y){
34712         this.view.headersDisabled = true;
34713         this.proxy.setHeight(this.view.mainWrap.getHeight());
34714         var w = this.cm.getColumnWidth(this.cellIndex);
34715         var minw = Math.max(w-this.grid.minColumnWidth, 0);
34716         this.resetConstraints();
34717         this.setXConstraint(minw, 1000);
34718         this.setYConstraint(0, 0);
34719         this.minX = x - minw;
34720         this.maxX = x + 1000;
34721         this.startPos = x;
34722         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
34723     },
34724
34725
34726     handleMouseDown : function(e){
34727         ev = Roo.EventObject.setEvent(e);
34728         var t = this.fly(ev.getTarget());
34729         if(t.hasClass("x-grid-split")){
34730             this.cellIndex = this.view.getCellIndex(t.dom);
34731             this.split = t.dom;
34732             this.cm = this.grid.colModel;
34733             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
34734                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
34735             }
34736         }
34737     },
34738
34739     endDrag : function(e){
34740         this.view.headersDisabled = false;
34741         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
34742         var diff = endX - this.startPos;
34743         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
34744     },
34745
34746     autoOffset : function(){
34747         this.setDelta(0,0);
34748     }
34749 });/*
34750  * Based on:
34751  * Ext JS Library 1.1.1
34752  * Copyright(c) 2006-2007, Ext JS, LLC.
34753  *
34754  * Originally Released Under LGPL - original licence link has changed is not relivant.
34755  *
34756  * Fork - LGPL
34757  * <script type="text/javascript">
34758  */
34759  
34760 // private
34761 // This is a support class used internally by the Grid components
34762 Roo.grid.GridDragZone = function(grid, config){
34763     this.view = grid.getView();
34764     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
34765     if(this.view.lockedBody){
34766         this.setHandleElId(Roo.id(this.view.mainBody.dom));
34767         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
34768     }
34769     this.scroll = false;
34770     this.grid = grid;
34771     this.ddel = document.createElement('div');
34772     this.ddel.className = 'x-grid-dd-wrap';
34773 };
34774
34775 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
34776     ddGroup : "GridDD",
34777
34778     getDragData : function(e){
34779         var t = Roo.lib.Event.getTarget(e);
34780         var rowIndex = this.view.findRowIndex(t);
34781         if(rowIndex !== false){
34782             var sm = this.grid.selModel;
34783             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
34784               //  sm.mouseDown(e, t);
34785             //}
34786             if (e.hasModifier()){
34787                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
34788             }
34789             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
34790         }
34791         return false;
34792     },
34793
34794     onInitDrag : function(e){
34795         var data = this.dragData;
34796         this.ddel.innerHTML = this.grid.getDragDropText();
34797         this.proxy.update(this.ddel);
34798         // fire start drag?
34799     },
34800
34801     afterRepair : function(){
34802         this.dragging = false;
34803     },
34804
34805     getRepairXY : function(e, data){
34806         return false;
34807     },
34808
34809     onEndDrag : function(data, e){
34810         // fire end drag?
34811     },
34812
34813     onValidDrop : function(dd, e, id){
34814         // fire drag drop?
34815         this.hideProxy();
34816     },
34817
34818     beforeInvalidDrop : function(e, id){
34819
34820     }
34821 });/*
34822  * Based on:
34823  * Ext JS Library 1.1.1
34824  * Copyright(c) 2006-2007, Ext JS, LLC.
34825  *
34826  * Originally Released Under LGPL - original licence link has changed is not relivant.
34827  *
34828  * Fork - LGPL
34829  * <script type="text/javascript">
34830  */
34831  
34832
34833 /**
34834  * @class Roo.grid.ColumnModel
34835  * @extends Roo.util.Observable
34836  * This is the default implementation of a ColumnModel used by the Grid. It defines
34837  * the columns in the grid.
34838  * <br>Usage:<br>
34839  <pre><code>
34840  var colModel = new Roo.grid.ColumnModel([
34841         {header: "Ticker", width: 60, sortable: true, locked: true},
34842         {header: "Company Name", width: 150, sortable: true},
34843         {header: "Market Cap.", width: 100, sortable: true},
34844         {header: "$ Sales", width: 100, sortable: true, renderer: money},
34845         {header: "Employees", width: 100, sortable: true, resizable: false}
34846  ]);
34847  </code></pre>
34848  * <p>
34849  
34850  * The config options listed for this class are options which may appear in each
34851  * individual column definition.
34852  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
34853  * @constructor
34854  * @param {Object} config An Array of column config objects. See this class's
34855  * config objects for details.
34856 */
34857 Roo.grid.ColumnModel = function(config){
34858         /**
34859      * The config passed into the constructor
34860      */
34861     this.config = config;
34862     this.lookup = {};
34863
34864     // if no id, create one
34865     // if the column does not have a dataIndex mapping,
34866     // map it to the order it is in the config
34867     for(var i = 0, len = config.length; i < len; i++){
34868         var c = config[i];
34869         if(typeof c.dataIndex == "undefined"){
34870             c.dataIndex = i;
34871         }
34872         if(typeof c.renderer == "string"){
34873             c.renderer = Roo.util.Format[c.renderer];
34874         }
34875         if(typeof c.id == "undefined"){
34876             c.id = Roo.id();
34877         }
34878         if(c.editor && c.editor.xtype){
34879             c.editor  = Roo.factory(c.editor, Roo.grid);
34880         }
34881         if(c.editor && c.editor.isFormField){
34882             c.editor = new Roo.grid.GridEditor(c.editor);
34883         }
34884         this.lookup[c.id] = c;
34885     }
34886
34887     /**
34888      * The width of columns which have no width specified (defaults to 100)
34889      * @type Number
34890      */
34891     this.defaultWidth = 100;
34892
34893     /**
34894      * Default sortable of columns which have no sortable specified (defaults to false)
34895      * @type Boolean
34896      */
34897     this.defaultSortable = false;
34898
34899     this.addEvents({
34900         /**
34901              * @event widthchange
34902              * Fires when the width of a column changes.
34903              * @param {ColumnModel} this
34904              * @param {Number} columnIndex The column index
34905              * @param {Number} newWidth The new width
34906              */
34907             "widthchange": true,
34908         /**
34909              * @event headerchange
34910              * Fires when the text of a header changes.
34911              * @param {ColumnModel} this
34912              * @param {Number} columnIndex The column index
34913              * @param {Number} newText The new header text
34914              */
34915             "headerchange": true,
34916         /**
34917              * @event hiddenchange
34918              * Fires when a column is hidden or "unhidden".
34919              * @param {ColumnModel} this
34920              * @param {Number} columnIndex The column index
34921              * @param {Boolean} hidden true if hidden, false otherwise
34922              */
34923             "hiddenchange": true,
34924             /**
34925          * @event columnmoved
34926          * Fires when a column is moved.
34927          * @param {ColumnModel} this
34928          * @param {Number} oldIndex
34929          * @param {Number} newIndex
34930          */
34931         "columnmoved" : true,
34932         /**
34933          * @event columlockchange
34934          * Fires when a column's locked state is changed
34935          * @param {ColumnModel} this
34936          * @param {Number} colIndex
34937          * @param {Boolean} locked true if locked
34938          */
34939         "columnlockchange" : true
34940     });
34941     Roo.grid.ColumnModel.superclass.constructor.call(this);
34942 };
34943 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
34944     /**
34945      * @cfg {String} header The header text to display in the Grid view.
34946      */
34947     /**
34948      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
34949      * {@link Roo.data.Record} definition from which to draw the column's value. If not
34950      * specified, the column's index is used as an index into the Record's data Array.
34951      */
34952     /**
34953      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
34954      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
34955      */
34956     /**
34957      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
34958      * Defaults to the value of the {@link #defaultSortable} property.
34959      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
34960      */
34961     /**
34962      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
34963      */
34964     /**
34965      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
34966      */
34967     /**
34968      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
34969      */
34970     /**
34971      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
34972      */
34973     /**
34974      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
34975      * given the cell's data value. See {@link #setRenderer}. If not specified, the
34976      * default renderer uses the raw data value.
34977      */
34978        /**
34979      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
34980      */
34981     /**
34982      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
34983      */
34984
34985     /**
34986      * Returns the id of the column at the specified index.
34987      * @param {Number} index The column index
34988      * @return {String} the id
34989      */
34990     getColumnId : function(index){
34991         return this.config[index].id;
34992     },
34993
34994     /**
34995      * Returns the column for a specified id.
34996      * @param {String} id The column id
34997      * @return {Object} the column
34998      */
34999     getColumnById : function(id){
35000         return this.lookup[id];
35001     },
35002
35003     
35004     /**
35005      * Returns the column for a specified dataIndex.
35006      * @param {String} dataIndex The column dataIndex
35007      * @return {Object|Boolean} the column or false if not found
35008      */
35009     getColumnByDataIndex: function(dataIndex){
35010         var index = this.findColumnIndex(dataIndex);
35011         return index > -1 ? this.config[index] : false;
35012     },
35013     
35014     /**
35015      * Returns the index for a specified column id.
35016      * @param {String} id The column id
35017      * @return {Number} the index, or -1 if not found
35018      */
35019     getIndexById : function(id){
35020         for(var i = 0, len = this.config.length; i < len; i++){
35021             if(this.config[i].id == id){
35022                 return i;
35023             }
35024         }
35025         return -1;
35026     },
35027     
35028     /**
35029      * Returns the index for a specified column dataIndex.
35030      * @param {String} dataIndex The column dataIndex
35031      * @return {Number} the index, or -1 if not found
35032      */
35033     
35034     findColumnIndex : function(dataIndex){
35035         for(var i = 0, len = this.config.length; i < len; i++){
35036             if(this.config[i].dataIndex == dataIndex){
35037                 return i;
35038             }
35039         }
35040         return -1;
35041     },
35042     
35043     
35044     moveColumn : function(oldIndex, newIndex){
35045         var c = this.config[oldIndex];
35046         this.config.splice(oldIndex, 1);
35047         this.config.splice(newIndex, 0, c);
35048         this.dataMap = null;
35049         this.fireEvent("columnmoved", this, oldIndex, newIndex);
35050     },
35051
35052     isLocked : function(colIndex){
35053         return this.config[colIndex].locked === true;
35054     },
35055
35056     setLocked : function(colIndex, value, suppressEvent){
35057         if(this.isLocked(colIndex) == value){
35058             return;
35059         }
35060         this.config[colIndex].locked = value;
35061         if(!suppressEvent){
35062             this.fireEvent("columnlockchange", this, colIndex, value);
35063         }
35064     },
35065
35066     getTotalLockedWidth : function(){
35067         var totalWidth = 0;
35068         for(var i = 0; i < this.config.length; i++){
35069             if(this.isLocked(i) && !this.isHidden(i)){
35070                 this.totalWidth += this.getColumnWidth(i);
35071             }
35072         }
35073         return totalWidth;
35074     },
35075
35076     getLockedCount : function(){
35077         for(var i = 0, len = this.config.length; i < len; i++){
35078             if(!this.isLocked(i)){
35079                 return i;
35080             }
35081         }
35082     },
35083
35084     /**
35085      * Returns the number of columns.
35086      * @return {Number}
35087      */
35088     getColumnCount : function(visibleOnly){
35089         if(visibleOnly === true){
35090             var c = 0;
35091             for(var i = 0, len = this.config.length; i < len; i++){
35092                 if(!this.isHidden(i)){
35093                     c++;
35094                 }
35095             }
35096             return c;
35097         }
35098         return this.config.length;
35099     },
35100
35101     /**
35102      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
35103      * @param {Function} fn
35104      * @param {Object} scope (optional)
35105      * @return {Array} result
35106      */
35107     getColumnsBy : function(fn, scope){
35108         var r = [];
35109         for(var i = 0, len = this.config.length; i < len; i++){
35110             var c = this.config[i];
35111             if(fn.call(scope||this, c, i) === true){
35112                 r[r.length] = c;
35113             }
35114         }
35115         return r;
35116     },
35117
35118     /**
35119      * Returns true if the specified column is sortable.
35120      * @param {Number} col The column index
35121      * @return {Boolean}
35122      */
35123     isSortable : function(col){
35124         if(typeof this.config[col].sortable == "undefined"){
35125             return this.defaultSortable;
35126         }
35127         return this.config[col].sortable;
35128     },
35129
35130     /**
35131      * Returns the rendering (formatting) function defined for the column.
35132      * @param {Number} col The column index.
35133      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
35134      */
35135     getRenderer : function(col){
35136         if(!this.config[col].renderer){
35137             return Roo.grid.ColumnModel.defaultRenderer;
35138         }
35139         return this.config[col].renderer;
35140     },
35141
35142     /**
35143      * Sets the rendering (formatting) function for a column.
35144      * @param {Number} col The column index
35145      * @param {Function} fn The function to use to process the cell's raw data
35146      * to return HTML markup for the grid view. The render function is called with
35147      * the following parameters:<ul>
35148      * <li>Data value.</li>
35149      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
35150      * <li>css A CSS style string to apply to the table cell.</li>
35151      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
35152      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
35153      * <li>Row index</li>
35154      * <li>Column index</li>
35155      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
35156      */
35157     setRenderer : function(col, fn){
35158         this.config[col].renderer = fn;
35159     },
35160
35161     /**
35162      * Returns the width for the specified column.
35163      * @param {Number} col The column index
35164      * @return {Number}
35165      */
35166     getColumnWidth : function(col){
35167         return this.config[col].width * 1 || this.defaultWidth;
35168     },
35169
35170     /**
35171      * Sets the width for a column.
35172      * @param {Number} col The column index
35173      * @param {Number} width The new width
35174      */
35175     setColumnWidth : function(col, width, suppressEvent){
35176         this.config[col].width = width;
35177         this.totalWidth = null;
35178         if(!suppressEvent){
35179              this.fireEvent("widthchange", this, col, width);
35180         }
35181     },
35182
35183     /**
35184      * Returns the total width of all columns.
35185      * @param {Boolean} includeHidden True to include hidden column widths
35186      * @return {Number}
35187      */
35188     getTotalWidth : function(includeHidden){
35189         if(!this.totalWidth){
35190             this.totalWidth = 0;
35191             for(var i = 0, len = this.config.length; i < len; i++){
35192                 if(includeHidden || !this.isHidden(i)){
35193                     this.totalWidth += this.getColumnWidth(i);
35194                 }
35195             }
35196         }
35197         return this.totalWidth;
35198     },
35199
35200     /**
35201      * Returns the header for the specified column.
35202      * @param {Number} col The column index
35203      * @return {String}
35204      */
35205     getColumnHeader : function(col){
35206         return this.config[col].header;
35207     },
35208
35209     /**
35210      * Sets the header for a column.
35211      * @param {Number} col The column index
35212      * @param {String} header The new header
35213      */
35214     setColumnHeader : function(col, header){
35215         this.config[col].header = header;
35216         this.fireEvent("headerchange", this, col, header);
35217     },
35218
35219     /**
35220      * Returns the tooltip for the specified column.
35221      * @param {Number} col The column index
35222      * @return {String}
35223      */
35224     getColumnTooltip : function(col){
35225             return this.config[col].tooltip;
35226     },
35227     /**
35228      * Sets the tooltip for a column.
35229      * @param {Number} col The column index
35230      * @param {String} tooltip The new tooltip
35231      */
35232     setColumnTooltip : function(col, tooltip){
35233             this.config[col].tooltip = tooltip;
35234     },
35235
35236     /**
35237      * Returns the dataIndex for the specified column.
35238      * @param {Number} col The column index
35239      * @return {Number}
35240      */
35241     getDataIndex : function(col){
35242         return this.config[col].dataIndex;
35243     },
35244
35245     /**
35246      * Sets the dataIndex for a column.
35247      * @param {Number} col The column index
35248      * @param {Number} dataIndex The new dataIndex
35249      */
35250     setDataIndex : function(col, dataIndex){
35251         this.config[col].dataIndex = dataIndex;
35252     },
35253
35254     
35255     
35256     /**
35257      * Returns true if the cell is editable.
35258      * @param {Number} colIndex The column index
35259      * @param {Number} rowIndex The row index
35260      * @return {Boolean}
35261      */
35262     isCellEditable : function(colIndex, rowIndex){
35263         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35264     },
35265
35266     /**
35267      * Returns the editor defined for the cell/column.
35268      * return false or null to disable editing.
35269      * @param {Number} colIndex The column index
35270      * @param {Number} rowIndex The row index
35271      * @return {Object}
35272      */
35273     getCellEditor : function(colIndex, rowIndex){
35274         return this.config[colIndex].editor;
35275     },
35276
35277     /**
35278      * Sets if a column is editable.
35279      * @param {Number} col The column index
35280      * @param {Boolean} editable True if the column is editable
35281      */
35282     setEditable : function(col, editable){
35283         this.config[col].editable = editable;
35284     },
35285
35286
35287     /**
35288      * Returns true if the column is hidden.
35289      * @param {Number} colIndex The column index
35290      * @return {Boolean}
35291      */
35292     isHidden : function(colIndex){
35293         return this.config[colIndex].hidden;
35294     },
35295
35296
35297     /**
35298      * Returns true if the column width cannot be changed
35299      */
35300     isFixed : function(colIndex){
35301         return this.config[colIndex].fixed;
35302     },
35303
35304     /**
35305      * Returns true if the column can be resized
35306      * @return {Boolean}
35307      */
35308     isResizable : function(colIndex){
35309         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35310     },
35311     /**
35312      * Sets if a column is hidden.
35313      * @param {Number} colIndex The column index
35314      * @param {Boolean} hidden True if the column is hidden
35315      */
35316     setHidden : function(colIndex, hidden){
35317         this.config[colIndex].hidden = hidden;
35318         this.totalWidth = null;
35319         this.fireEvent("hiddenchange", this, colIndex, hidden);
35320     },
35321
35322     /**
35323      * Sets the editor for a column.
35324      * @param {Number} col The column index
35325      * @param {Object} editor The editor object
35326      */
35327     setEditor : function(col, editor){
35328         this.config[col].editor = editor;
35329     }
35330 });
35331
35332 Roo.grid.ColumnModel.defaultRenderer = function(value){
35333         if(typeof value == "string" && value.length < 1){
35334             return "&#160;";
35335         }
35336         return value;
35337 };
35338
35339 // Alias for backwards compatibility
35340 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35341 /*
35342  * Based on:
35343  * Ext JS Library 1.1.1
35344  * Copyright(c) 2006-2007, Ext JS, LLC.
35345  *
35346  * Originally Released Under LGPL - original licence link has changed is not relivant.
35347  *
35348  * Fork - LGPL
35349  * <script type="text/javascript">
35350  */
35351
35352 /**
35353  * @class Roo.grid.AbstractSelectionModel
35354  * @extends Roo.util.Observable
35355  * Abstract base class for grid SelectionModels.  It provides the interface that should be
35356  * implemented by descendant classes.  This class should not be directly instantiated.
35357  * @constructor
35358  */
35359 Roo.grid.AbstractSelectionModel = function(){
35360     this.locked = false;
35361     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35362 };
35363
35364 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
35365     /** @ignore Called by the grid automatically. Do not call directly. */
35366     init : function(grid){
35367         this.grid = grid;
35368         this.initEvents();
35369     },
35370
35371     /**
35372      * Locks the selections.
35373      */
35374     lock : function(){
35375         this.locked = true;
35376     },
35377
35378     /**
35379      * Unlocks the selections.
35380      */
35381     unlock : function(){
35382         this.locked = false;
35383     },
35384
35385     /**
35386      * Returns true if the selections are locked.
35387      * @return {Boolean}
35388      */
35389     isLocked : function(){
35390         return this.locked;
35391     }
35392 });/*
35393  * Based on:
35394  * Ext JS Library 1.1.1
35395  * Copyright(c) 2006-2007, Ext JS, LLC.
35396  *
35397  * Originally Released Under LGPL - original licence link has changed is not relivant.
35398  *
35399  * Fork - LGPL
35400  * <script type="text/javascript">
35401  */
35402 /**
35403  * @extends Roo.grid.AbstractSelectionModel
35404  * @class Roo.grid.RowSelectionModel
35405  * The default SelectionModel used by {@link Roo.grid.Grid}.
35406  * It supports multiple selections and keyboard selection/navigation. 
35407  * @constructor
35408  * @param {Object} config
35409  */
35410 Roo.grid.RowSelectionModel = function(config){
35411     Roo.apply(this, config);
35412     this.selections = new Roo.util.MixedCollection(false, function(o){
35413         return o.id;
35414     });
35415
35416     this.last = false;
35417     this.lastActive = false;
35418
35419     this.addEvents({
35420         /**
35421              * @event selectionchange
35422              * Fires when the selection changes
35423              * @param {SelectionModel} this
35424              */
35425             "selectionchange" : true,
35426         /**
35427              * @event afterselectionchange
35428              * Fires after the selection changes (eg. by key press or clicking)
35429              * @param {SelectionModel} this
35430              */
35431             "afterselectionchange" : true,
35432         /**
35433              * @event beforerowselect
35434              * Fires when a row is selected being selected, return false to cancel.
35435              * @param {SelectionModel} this
35436              * @param {Number} rowIndex The selected index
35437              * @param {Boolean} keepExisting False if other selections will be cleared
35438              */
35439             "beforerowselect" : true,
35440         /**
35441              * @event rowselect
35442              * Fires when a row is selected.
35443              * @param {SelectionModel} this
35444              * @param {Number} rowIndex The selected index
35445              * @param {Roo.data.Record} r The record
35446              */
35447             "rowselect" : true,
35448         /**
35449              * @event rowdeselect
35450              * Fires when a row is deselected.
35451              * @param {SelectionModel} this
35452              * @param {Number} rowIndex The selected index
35453              */
35454         "rowdeselect" : true
35455     });
35456     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
35457     this.locked = false;
35458 };
35459
35460 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
35461     /**
35462      * @cfg {Boolean} singleSelect
35463      * True to allow selection of only one row at a time (defaults to false)
35464      */
35465     singleSelect : false,
35466
35467     // private
35468     initEvents : function(){
35469
35470         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
35471             this.grid.on("mousedown", this.handleMouseDown, this);
35472         }else{ // allow click to work like normal
35473             this.grid.on("rowclick", this.handleDragableRowClick, this);
35474         }
35475
35476         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
35477             "up" : function(e){
35478                 if(!e.shiftKey){
35479                     this.selectPrevious(e.shiftKey);
35480                 }else if(this.last !== false && this.lastActive !== false){
35481                     var last = this.last;
35482                     this.selectRange(this.last,  this.lastActive-1);
35483                     this.grid.getView().focusRow(this.lastActive);
35484                     if(last !== false){
35485                         this.last = last;
35486                     }
35487                 }else{
35488                     this.selectFirstRow();
35489                 }
35490                 this.fireEvent("afterselectionchange", this);
35491             },
35492             "down" : function(e){
35493                 if(!e.shiftKey){
35494                     this.selectNext(e.shiftKey);
35495                 }else if(this.last !== false && this.lastActive !== false){
35496                     var last = this.last;
35497                     this.selectRange(this.last,  this.lastActive+1);
35498                     this.grid.getView().focusRow(this.lastActive);
35499                     if(last !== false){
35500                         this.last = last;
35501                     }
35502                 }else{
35503                     this.selectFirstRow();
35504                 }
35505                 this.fireEvent("afterselectionchange", this);
35506             },
35507             scope: this
35508         });
35509
35510         var view = this.grid.view;
35511         view.on("refresh", this.onRefresh, this);
35512         view.on("rowupdated", this.onRowUpdated, this);
35513         view.on("rowremoved", this.onRemove, this);
35514     },
35515
35516     // private
35517     onRefresh : function(){
35518         var ds = this.grid.dataSource, i, v = this.grid.view;
35519         var s = this.selections;
35520         s.each(function(r){
35521             if((i = ds.indexOfId(r.id)) != -1){
35522                 v.onRowSelect(i);
35523             }else{
35524                 s.remove(r);
35525             }
35526         });
35527     },
35528
35529     // private
35530     onRemove : function(v, index, r){
35531         this.selections.remove(r);
35532     },
35533
35534     // private
35535     onRowUpdated : function(v, index, r){
35536         if(this.isSelected(r)){
35537             v.onRowSelect(index);
35538         }
35539     },
35540
35541     /**
35542      * Select records.
35543      * @param {Array} records The records to select
35544      * @param {Boolean} keepExisting (optional) True to keep existing selections
35545      */
35546     selectRecords : function(records, keepExisting){
35547         if(!keepExisting){
35548             this.clearSelections();
35549         }
35550         var ds = this.grid.dataSource;
35551         for(var i = 0, len = records.length; i < len; i++){
35552             this.selectRow(ds.indexOf(records[i]), true);
35553         }
35554     },
35555
35556     /**
35557      * Gets the number of selected rows.
35558      * @return {Number}
35559      */
35560     getCount : function(){
35561         return this.selections.length;
35562     },
35563
35564     /**
35565      * Selects the first row in the grid.
35566      */
35567     selectFirstRow : function(){
35568         this.selectRow(0);
35569     },
35570
35571     /**
35572      * Select the last row.
35573      * @param {Boolean} keepExisting (optional) True to keep existing selections
35574      */
35575     selectLastRow : function(keepExisting){
35576         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
35577     },
35578
35579     /**
35580      * Selects the row immediately following the last selected row.
35581      * @param {Boolean} keepExisting (optional) True to keep existing selections
35582      */
35583     selectNext : function(keepExisting){
35584         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
35585             this.selectRow(this.last+1, keepExisting);
35586             this.grid.getView().focusRow(this.last);
35587         }
35588     },
35589
35590     /**
35591      * Selects the row that precedes the last selected row.
35592      * @param {Boolean} keepExisting (optional) True to keep existing selections
35593      */
35594     selectPrevious : function(keepExisting){
35595         if(this.last){
35596             this.selectRow(this.last-1, keepExisting);
35597             this.grid.getView().focusRow(this.last);
35598         }
35599     },
35600
35601     /**
35602      * Returns the selected records
35603      * @return {Array} Array of selected records
35604      */
35605     getSelections : function(){
35606         return [].concat(this.selections.items);
35607     },
35608
35609     /**
35610      * Returns the first selected record.
35611      * @return {Record}
35612      */
35613     getSelected : function(){
35614         return this.selections.itemAt(0);
35615     },
35616
35617
35618     /**
35619      * Clears all selections.
35620      */
35621     clearSelections : function(fast){
35622         if(this.locked) return;
35623         if(fast !== true){
35624             var ds = this.grid.dataSource;
35625             var s = this.selections;
35626             s.each(function(r){
35627                 this.deselectRow(ds.indexOfId(r.id));
35628             }, this);
35629             s.clear();
35630         }else{
35631             this.selections.clear();
35632         }
35633         this.last = false;
35634     },
35635
35636
35637     /**
35638      * Selects all rows.
35639      */
35640     selectAll : function(){
35641         if(this.locked) return;
35642         this.selections.clear();
35643         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
35644             this.selectRow(i, true);
35645         }
35646     },
35647
35648     /**
35649      * Returns True if there is a selection.
35650      * @return {Boolean}
35651      */
35652     hasSelection : function(){
35653         return this.selections.length > 0;
35654     },
35655
35656     /**
35657      * Returns True if the specified row is selected.
35658      * @param {Number/Record} record The record or index of the record to check
35659      * @return {Boolean}
35660      */
35661     isSelected : function(index){
35662         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
35663         return (r && this.selections.key(r.id) ? true : false);
35664     },
35665
35666     /**
35667      * Returns True if the specified record id is selected.
35668      * @param {String} id The id of record to check
35669      * @return {Boolean}
35670      */
35671     isIdSelected : function(id){
35672         return (this.selections.key(id) ? true : false);
35673     },
35674
35675     // private
35676     handleMouseDown : function(e, t){
35677         var view = this.grid.getView(), rowIndex;
35678         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
35679             return;
35680         };
35681         if(e.shiftKey && this.last !== false){
35682             var last = this.last;
35683             this.selectRange(last, rowIndex, e.ctrlKey);
35684             this.last = last; // reset the last
35685             view.focusRow(rowIndex);
35686         }else{
35687             var isSelected = this.isSelected(rowIndex);
35688             if(e.button !== 0 && isSelected){
35689                 view.focusRow(rowIndex);
35690             }else if(e.ctrlKey && isSelected){
35691                 this.deselectRow(rowIndex);
35692             }else if(!isSelected){
35693                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
35694                 view.focusRow(rowIndex);
35695             }
35696         }
35697         this.fireEvent("afterselectionchange", this);
35698     },
35699     // private
35700     handleDragableRowClick :  function(grid, rowIndex, e) 
35701     {
35702         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
35703             this.selectRow(rowIndex, false);
35704             grid.view.focusRow(rowIndex);
35705              this.fireEvent("afterselectionchange", this);
35706         }
35707     },
35708     
35709     /**
35710      * Selects multiple rows.
35711      * @param {Array} rows Array of the indexes of the row to select
35712      * @param {Boolean} keepExisting (optional) True to keep existing selections
35713      */
35714     selectRows : function(rows, keepExisting){
35715         if(!keepExisting){
35716             this.clearSelections();
35717         }
35718         for(var i = 0, len = rows.length; i < len; i++){
35719             this.selectRow(rows[i], true);
35720         }
35721     },
35722
35723     /**
35724      * Selects a range of rows. All rows in between startRow and endRow are also selected.
35725      * @param {Number} startRow The index of the first row in the range
35726      * @param {Number} endRow The index of the last row in the range
35727      * @param {Boolean} keepExisting (optional) True to retain existing selections
35728      */
35729     selectRange : function(startRow, endRow, keepExisting){
35730         if(this.locked) return;
35731         if(!keepExisting){
35732             this.clearSelections();
35733         }
35734         if(startRow <= endRow){
35735             for(var i = startRow; i <= endRow; i++){
35736                 this.selectRow(i, true);
35737             }
35738         }else{
35739             for(var i = startRow; i >= endRow; i--){
35740                 this.selectRow(i, true);
35741             }
35742         }
35743     },
35744
35745     /**
35746      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
35747      * @param {Number} startRow The index of the first row in the range
35748      * @param {Number} endRow The index of the last row in the range
35749      */
35750     deselectRange : function(startRow, endRow, preventViewNotify){
35751         if(this.locked) return;
35752         for(var i = startRow; i <= endRow; i++){
35753             this.deselectRow(i, preventViewNotify);
35754         }
35755     },
35756
35757     /**
35758      * Selects a row.
35759      * @param {Number} row The index of the row to select
35760      * @param {Boolean} keepExisting (optional) True to keep existing selections
35761      */
35762     selectRow : function(index, keepExisting, preventViewNotify){
35763         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
35764         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
35765             if(!keepExisting || this.singleSelect){
35766                 this.clearSelections();
35767             }
35768             var r = this.grid.dataSource.getAt(index);
35769             this.selections.add(r);
35770             this.last = this.lastActive = index;
35771             if(!preventViewNotify){
35772                 this.grid.getView().onRowSelect(index);
35773             }
35774             this.fireEvent("rowselect", this, index, r);
35775             this.fireEvent("selectionchange", this);
35776         }
35777     },
35778
35779     /**
35780      * Deselects a row.
35781      * @param {Number} row The index of the row to deselect
35782      */
35783     deselectRow : function(index, preventViewNotify){
35784         if(this.locked) return;
35785         if(this.last == index){
35786             this.last = false;
35787         }
35788         if(this.lastActive == index){
35789             this.lastActive = false;
35790         }
35791         var r = this.grid.dataSource.getAt(index);
35792         this.selections.remove(r);
35793         if(!preventViewNotify){
35794             this.grid.getView().onRowDeselect(index);
35795         }
35796         this.fireEvent("rowdeselect", this, index);
35797         this.fireEvent("selectionchange", this);
35798     },
35799
35800     // private
35801     restoreLast : function(){
35802         if(this._last){
35803             this.last = this._last;
35804         }
35805     },
35806
35807     // private
35808     acceptsNav : function(row, col, cm){
35809         return !cm.isHidden(col) && cm.isCellEditable(col, row);
35810     },
35811
35812     // private
35813     onEditorKey : function(field, e){
35814         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
35815         if(k == e.TAB){
35816             e.stopEvent();
35817             ed.completeEdit();
35818             if(e.shiftKey){
35819                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35820             }else{
35821                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35822             }
35823         }else if(k == e.ENTER && !e.ctrlKey){
35824             e.stopEvent();
35825             ed.completeEdit();
35826             if(e.shiftKey){
35827                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
35828             }else{
35829                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
35830             }
35831         }else if(k == e.ESC){
35832             ed.cancelEdit();
35833         }
35834         if(newCell){
35835             g.startEditing(newCell[0], newCell[1]);
35836         }
35837     }
35838 });/*
35839  * Based on:
35840  * Ext JS Library 1.1.1
35841  * Copyright(c) 2006-2007, Ext JS, LLC.
35842  *
35843  * Originally Released Under LGPL - original licence link has changed is not relivant.
35844  *
35845  * Fork - LGPL
35846  * <script type="text/javascript">
35847  */
35848 /**
35849  * @class Roo.grid.CellSelectionModel
35850  * @extends Roo.grid.AbstractSelectionModel
35851  * This class provides the basic implementation for cell selection in a grid.
35852  * @constructor
35853  * @param {Object} config The object containing the configuration of this model.
35854  */
35855 Roo.grid.CellSelectionModel = function(config){
35856     Roo.apply(this, config);
35857
35858     this.selection = null;
35859
35860     this.addEvents({
35861         /**
35862              * @event beforerowselect
35863              * Fires before a cell is selected.
35864              * @param {SelectionModel} this
35865              * @param {Number} rowIndex The selected row index
35866              * @param {Number} colIndex The selected cell index
35867              */
35868             "beforecellselect" : true,
35869         /**
35870              * @event cellselect
35871              * Fires when a cell is selected.
35872              * @param {SelectionModel} this
35873              * @param {Number} rowIndex The selected row index
35874              * @param {Number} colIndex The selected cell index
35875              */
35876             "cellselect" : true,
35877         /**
35878              * @event selectionchange
35879              * Fires when the active selection changes.
35880              * @param {SelectionModel} this
35881              * @param {Object} selection null for no selection or an object (o) with two properties
35882                 <ul>
35883                 <li>o.record: the record object for the row the selection is in</li>
35884                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
35885                 </ul>
35886              */
35887             "selectionchange" : true
35888     });
35889     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
35890 };
35891
35892 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
35893
35894     /** @ignore */
35895     initEvents : function(){
35896         this.grid.on("mousedown", this.handleMouseDown, this);
35897         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
35898         var view = this.grid.view;
35899         view.on("refresh", this.onViewChange, this);
35900         view.on("rowupdated", this.onRowUpdated, this);
35901         view.on("beforerowremoved", this.clearSelections, this);
35902         view.on("beforerowsinserted", this.clearSelections, this);
35903         if(this.grid.isEditor){
35904             this.grid.on("beforeedit", this.beforeEdit,  this);
35905         }
35906     },
35907
35908         //private
35909     beforeEdit : function(e){
35910         this.select(e.row, e.column, false, true, e.record);
35911     },
35912
35913         //private
35914     onRowUpdated : function(v, index, r){
35915         if(this.selection && this.selection.record == r){
35916             v.onCellSelect(index, this.selection.cell[1]);
35917         }
35918     },
35919
35920         //private
35921     onViewChange : function(){
35922         this.clearSelections(true);
35923     },
35924
35925         /**
35926          * Returns the currently selected cell,.
35927          * @return {Array} The selected cell (row, column) or null if none selected.
35928          */
35929     getSelectedCell : function(){
35930         return this.selection ? this.selection.cell : null;
35931     },
35932
35933     /**
35934      * Clears all selections.
35935      * @param {Boolean} true to prevent the gridview from being notified about the change.
35936      */
35937     clearSelections : function(preventNotify){
35938         var s = this.selection;
35939         if(s){
35940             if(preventNotify !== true){
35941                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
35942             }
35943             this.selection = null;
35944             this.fireEvent("selectionchange", this, null);
35945         }
35946     },
35947
35948     /**
35949      * Returns true if there is a selection.
35950      * @return {Boolean}
35951      */
35952     hasSelection : function(){
35953         return this.selection ? true : false;
35954     },
35955
35956     /** @ignore */
35957     handleMouseDown : function(e, t){
35958         var v = this.grid.getView();
35959         if(this.isLocked()){
35960             return;
35961         };
35962         var row = v.findRowIndex(t);
35963         var cell = v.findCellIndex(t);
35964         if(row !== false && cell !== false){
35965             this.select(row, cell);
35966         }
35967     },
35968
35969     /**
35970      * Selects a cell.
35971      * @param {Number} rowIndex
35972      * @param {Number} collIndex
35973      */
35974     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
35975         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
35976             this.clearSelections();
35977             r = r || this.grid.dataSource.getAt(rowIndex);
35978             this.selection = {
35979                 record : r,
35980                 cell : [rowIndex, colIndex]
35981             };
35982             if(!preventViewNotify){
35983                 var v = this.grid.getView();
35984                 v.onCellSelect(rowIndex, colIndex);
35985                 if(preventFocus !== true){
35986                     v.focusCell(rowIndex, colIndex);
35987                 }
35988             }
35989             this.fireEvent("cellselect", this, rowIndex, colIndex);
35990             this.fireEvent("selectionchange", this, this.selection);
35991         }
35992     },
35993
35994         //private
35995     isSelectable : function(rowIndex, colIndex, cm){
35996         return !cm.isHidden(colIndex);
35997     },
35998
35999     /** @ignore */
36000     handleKeyDown : function(e){
36001         Roo.log('Cell Sel Model handleKeyDown');
36002         if(!e.isNavKeyPress()){
36003             return;
36004         }
36005         var g = this.grid, s = this.selection;
36006         if(!s){
36007             e.stopEvent();
36008             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
36009             if(cell){
36010                 this.select(cell[0], cell[1]);
36011             }
36012             return;
36013         }
36014         var sm = this;
36015         var walk = function(row, col, step){
36016             return g.walkCells(row, col, step, sm.isSelectable,  sm);
36017         };
36018         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
36019         var newCell;
36020
36021         switch(k){
36022             case e.TAB:
36023                 // handled by onEditorKey
36024                 if (g.isEditor && g.editing) {
36025                     return;
36026                 }
36027                 if(e.shiftKey){
36028                      newCell = walk(r, c-1, -1);
36029                 }else{
36030                      newCell = walk(r, c+1, 1);
36031                 }
36032              break;
36033              case e.DOWN:
36034                  newCell = walk(r+1, c, 1);
36035              break;
36036              case e.UP:
36037                  newCell = walk(r-1, c, -1);
36038              break;
36039              case e.RIGHT:
36040                  newCell = walk(r, c+1, 1);
36041              break;
36042              case e.LEFT:
36043                  newCell = walk(r, c-1, -1);
36044              break;
36045              case e.ENTER:
36046                  if(g.isEditor && !g.editing){
36047                     g.startEditing(r, c);
36048                     e.stopEvent();
36049                     return;
36050                 }
36051              break;
36052         };
36053         if(newCell){
36054             this.select(newCell[0], newCell[1]);
36055             e.stopEvent();
36056         }
36057     },
36058
36059     acceptsNav : function(row, col, cm){
36060         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36061     },
36062
36063     onEditorKey : function(field, e){
36064         
36065         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
36066         ///Roo.log('onEditorKey' + k);
36067         
36068         if(k == e.TAB){
36069             if(e.shiftKey){
36070                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36071             }else{
36072                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36073             }
36074             e.stopEvent();
36075         }else if(k == e.ENTER && !e.ctrlKey){
36076             ed.completeEdit();
36077             e.stopEvent();
36078             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36079         }else if(k == e.ESC){
36080             ed.cancelEdit();
36081         }
36082         
36083         
36084         if(newCell){
36085             //Roo.log('next cell after edit');
36086             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
36087         }
36088     }
36089 });/*
36090  * Based on:
36091  * Ext JS Library 1.1.1
36092  * Copyright(c) 2006-2007, Ext JS, LLC.
36093  *
36094  * Originally Released Under LGPL - original licence link has changed is not relivant.
36095  *
36096  * Fork - LGPL
36097  * <script type="text/javascript">
36098  */
36099  
36100 /**
36101  * @class Roo.grid.EditorGrid
36102  * @extends Roo.grid.Grid
36103  * Class for creating and editable grid.
36104  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
36105  * The container MUST have some type of size defined for the grid to fill. The container will be 
36106  * automatically set to position relative if it isn't already.
36107  * @param {Object} dataSource The data model to bind to
36108  * @param {Object} colModel The column model with info about this grid's columns
36109  */
36110 Roo.grid.EditorGrid = function(container, config){
36111     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
36112     this.getGridEl().addClass("xedit-grid");
36113
36114     if(!this.selModel){
36115         this.selModel = new Roo.grid.CellSelectionModel();
36116     }
36117
36118     this.activeEditor = null;
36119
36120         this.addEvents({
36121             /**
36122              * @event beforeedit
36123              * Fires before cell editing is triggered. The edit event object has the following properties <br />
36124              * <ul style="padding:5px;padding-left:16px;">
36125              * <li>grid - This grid</li>
36126              * <li>record - The record being edited</li>
36127              * <li>field - The field name being edited</li>
36128              * <li>value - The value for the field being edited.</li>
36129              * <li>row - The grid row index</li>
36130              * <li>column - The grid column index</li>
36131              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36132              * </ul>
36133              * @param {Object} e An edit event (see above for description)
36134              */
36135             "beforeedit" : true,
36136             /**
36137              * @event afteredit
36138              * Fires after a cell is edited. <br />
36139              * <ul style="padding:5px;padding-left:16px;">
36140              * <li>grid - This grid</li>
36141              * <li>record - The record being edited</li>
36142              * <li>field - The field name being edited</li>
36143              * <li>value - The value being set</li>
36144              * <li>originalValue - The original value for the field, before the edit.</li>
36145              * <li>row - The grid row index</li>
36146              * <li>column - The grid column index</li>
36147              * </ul>
36148              * @param {Object} e An edit event (see above for description)
36149              */
36150             "afteredit" : true,
36151             /**
36152              * @event validateedit
36153              * Fires after a cell is edited, but before the value is set in the record. 
36154          * You can use this to modify the value being set in the field, Return false
36155              * to cancel the change. The edit event object has the following properties <br />
36156              * <ul style="padding:5px;padding-left:16px;">
36157          * <li>editor - This editor</li>
36158              * <li>grid - This grid</li>
36159              * <li>record - The record being edited</li>
36160              * <li>field - The field name being edited</li>
36161              * <li>value - The value being set</li>
36162              * <li>originalValue - The original value for the field, before the edit.</li>
36163              * <li>row - The grid row index</li>
36164              * <li>column - The grid column index</li>
36165              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36166              * </ul>
36167              * @param {Object} e An edit event (see above for description)
36168              */
36169             "validateedit" : true
36170         });
36171     this.on("bodyscroll", this.stopEditing,  this);
36172     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
36173 };
36174
36175 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36176     /**
36177      * @cfg {Number} clicksToEdit
36178      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36179      */
36180     clicksToEdit: 2,
36181
36182     // private
36183     isEditor : true,
36184     // private
36185     trackMouseOver: false, // causes very odd FF errors
36186
36187     onCellDblClick : function(g, row, col){
36188         this.startEditing(row, col);
36189     },
36190
36191     onEditComplete : function(ed, value, startValue){
36192         this.editing = false;
36193         this.activeEditor = null;
36194         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36195         var r = ed.record;
36196         var field = this.colModel.getDataIndex(ed.col);
36197         var e = {
36198             grid: this,
36199             record: r,
36200             field: field,
36201             originalValue: startValue,
36202             value: value,
36203             row: ed.row,
36204             column: ed.col,
36205             cancel:false,
36206             editor: ed
36207         };
36208         if(String(value) !== String(startValue)){
36209             
36210             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36211                 r.set(field, e.value);
36212                 // if we are dealing with a combo box..
36213                 // then we also set the 'name' colum to be the displayField
36214                 if (ed.field.displayField && ed.field.name) {
36215                     r.set(ed.field.name, ed.field.el.dom.value);
36216                 }
36217                 
36218                 delete e.cancel; //?? why!!!
36219                 this.fireEvent("afteredit", e);
36220             }
36221         } else {
36222             this.fireEvent("afteredit", e); // always fire it!
36223         }
36224         this.view.focusCell(ed.row, ed.col);
36225     },
36226
36227     /**
36228      * Starts editing the specified for the specified row/column
36229      * @param {Number} rowIndex
36230      * @param {Number} colIndex
36231      */
36232     startEditing : function(row, col){
36233         this.stopEditing();
36234         if(this.colModel.isCellEditable(col, row)){
36235             this.view.ensureVisible(row, col, true);
36236             var r = this.dataSource.getAt(row);
36237             var field = this.colModel.getDataIndex(col);
36238             var e = {
36239                 grid: this,
36240                 record: r,
36241                 field: field,
36242                 value: r.data[field],
36243                 row: row,
36244                 column: col,
36245                 cancel:false
36246             };
36247             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36248                 this.editing = true;
36249                 var ed = this.colModel.getCellEditor(col, row);
36250                 
36251                 if (!ed) {
36252                     return;
36253                 }
36254                 if(!ed.rendered){
36255                     ed.render(ed.parentEl || document.body);
36256                 }
36257                 ed.field.reset();
36258                 (function(){ // complex but required for focus issues in safari, ie and opera
36259                     ed.row = row;
36260                     ed.col = col;
36261                     ed.record = r;
36262                     ed.on("complete", this.onEditComplete, this, {single: true});
36263                     ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
36264                     this.activeEditor = ed;
36265                     var v = r.data[field];
36266                     ed.startEdit(this.view.getCell(row, col), v);
36267                     // combo's with 'displayField and name set
36268                     if (ed.field.displayField && ed.field.name) {
36269                         ed.field.el.dom.value = r.data[ed.field.name];
36270                     }
36271                     
36272                     
36273                 }).defer(50, this);
36274             }
36275         }
36276     },
36277         
36278     /**
36279      * Stops any active editing
36280      */
36281     stopEditing : function(){
36282         if(this.activeEditor){
36283             this.activeEditor.completeEdit();
36284         }
36285         this.activeEditor = null;
36286     }
36287 });/*
36288  * Based on:
36289  * Ext JS Library 1.1.1
36290  * Copyright(c) 2006-2007, Ext JS, LLC.
36291  *
36292  * Originally Released Under LGPL - original licence link has changed is not relivant.
36293  *
36294  * Fork - LGPL
36295  * <script type="text/javascript">
36296  */
36297
36298 // private - not really -- you end up using it !
36299 // This is a support class used internally by the Grid components
36300
36301 /**
36302  * @class Roo.grid.GridEditor
36303  * @extends Roo.Editor
36304  * Class for creating and editable grid elements.
36305  * @param {Object} config any settings (must include field)
36306  */
36307 Roo.grid.GridEditor = function(field, config){
36308     if (!config && field.field) {
36309         config = field;
36310         field = Roo.factory(config.field, Roo.form);
36311     }
36312     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36313     field.monitorTab = false;
36314 };
36315
36316 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36317     
36318     /**
36319      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36320      */
36321     
36322     alignment: "tl-tl",
36323     autoSize: "width",
36324     hideEl : false,
36325     cls: "x-small-editor x-grid-editor",
36326     shim:false,
36327     shadow:"frame"
36328 });/*
36329  * Based on:
36330  * Ext JS Library 1.1.1
36331  * Copyright(c) 2006-2007, Ext JS, LLC.
36332  *
36333  * Originally Released Under LGPL - original licence link has changed is not relivant.
36334  *
36335  * Fork - LGPL
36336  * <script type="text/javascript">
36337  */
36338   
36339
36340   
36341 Roo.grid.PropertyRecord = Roo.data.Record.create([
36342     {name:'name',type:'string'},  'value'
36343 ]);
36344
36345
36346 Roo.grid.PropertyStore = function(grid, source){
36347     this.grid = grid;
36348     this.store = new Roo.data.Store({
36349         recordType : Roo.grid.PropertyRecord
36350     });
36351     this.store.on('update', this.onUpdate,  this);
36352     if(source){
36353         this.setSource(source);
36354     }
36355     Roo.grid.PropertyStore.superclass.constructor.call(this);
36356 };
36357
36358
36359
36360 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
36361     setSource : function(o){
36362         this.source = o;
36363         this.store.removeAll();
36364         var data = [];
36365         for(var k in o){
36366             if(this.isEditableValue(o[k])){
36367                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
36368             }
36369         }
36370         this.store.loadRecords({records: data}, {}, true);
36371     },
36372
36373     onUpdate : function(ds, record, type){
36374         if(type == Roo.data.Record.EDIT){
36375             var v = record.data['value'];
36376             var oldValue = record.modified['value'];
36377             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
36378                 this.source[record.id] = v;
36379                 record.commit();
36380                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
36381             }else{
36382                 record.reject();
36383             }
36384         }
36385     },
36386
36387     getProperty : function(row){
36388        return this.store.getAt(row);
36389     },
36390
36391     isEditableValue: function(val){
36392         if(val && val instanceof Date){
36393             return true;
36394         }else if(typeof val == 'object' || typeof val == 'function'){
36395             return false;
36396         }
36397         return true;
36398     },
36399
36400     setValue : function(prop, value){
36401         this.source[prop] = value;
36402         this.store.getById(prop).set('value', value);
36403     },
36404
36405     getSource : function(){
36406         return this.source;
36407     }
36408 });
36409
36410 Roo.grid.PropertyColumnModel = function(grid, store){
36411     this.grid = grid;
36412     var g = Roo.grid;
36413     g.PropertyColumnModel.superclass.constructor.call(this, [
36414         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
36415         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
36416     ]);
36417     this.store = store;
36418     this.bselect = Roo.DomHelper.append(document.body, {
36419         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
36420             {tag: 'option', value: 'true', html: 'true'},
36421             {tag: 'option', value: 'false', html: 'false'}
36422         ]
36423     });
36424     Roo.id(this.bselect);
36425     var f = Roo.form;
36426     this.editors = {
36427         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
36428         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
36429         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
36430         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
36431         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
36432     };
36433     this.renderCellDelegate = this.renderCell.createDelegate(this);
36434     this.renderPropDelegate = this.renderProp.createDelegate(this);
36435 };
36436
36437 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
36438     
36439     
36440     nameText : 'Name',
36441     valueText : 'Value',
36442     
36443     dateFormat : 'm/j/Y',
36444     
36445     
36446     renderDate : function(dateVal){
36447         return dateVal.dateFormat(this.dateFormat);
36448     },
36449
36450     renderBool : function(bVal){
36451         return bVal ? 'true' : 'false';
36452     },
36453
36454     isCellEditable : function(colIndex, rowIndex){
36455         return colIndex == 1;
36456     },
36457
36458     getRenderer : function(col){
36459         return col == 1 ?
36460             this.renderCellDelegate : this.renderPropDelegate;
36461     },
36462
36463     renderProp : function(v){
36464         return this.getPropertyName(v);
36465     },
36466
36467     renderCell : function(val){
36468         var rv = val;
36469         if(val instanceof Date){
36470             rv = this.renderDate(val);
36471         }else if(typeof val == 'boolean'){
36472             rv = this.renderBool(val);
36473         }
36474         return Roo.util.Format.htmlEncode(rv);
36475     },
36476
36477     getPropertyName : function(name){
36478         var pn = this.grid.propertyNames;
36479         return pn && pn[name] ? pn[name] : name;
36480     },
36481
36482     getCellEditor : function(colIndex, rowIndex){
36483         var p = this.store.getProperty(rowIndex);
36484         var n = p.data['name'], val = p.data['value'];
36485         
36486         if(typeof(this.grid.customEditors[n]) == 'string'){
36487             return this.editors[this.grid.customEditors[n]];
36488         }
36489         if(typeof(this.grid.customEditors[n]) != 'undefined'){
36490             return this.grid.customEditors[n];
36491         }
36492         if(val instanceof Date){
36493             return this.editors['date'];
36494         }else if(typeof val == 'number'){
36495             return this.editors['number'];
36496         }else if(typeof val == 'boolean'){
36497             return this.editors['boolean'];
36498         }else{
36499             return this.editors['string'];
36500         }
36501     }
36502 });
36503
36504 /**
36505  * @class Roo.grid.PropertyGrid
36506  * @extends Roo.grid.EditorGrid
36507  * This class represents the  interface of a component based property grid control.
36508  * <br><br>Usage:<pre><code>
36509  var grid = new Roo.grid.PropertyGrid("my-container-id", {
36510       
36511  });
36512  // set any options
36513  grid.render();
36514  * </code></pre>
36515   
36516  * @constructor
36517  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36518  * The container MUST have some type of size defined for the grid to fill. The container will be
36519  * automatically set to position relative if it isn't already.
36520  * @param {Object} config A config object that sets properties on this grid.
36521  */
36522 Roo.grid.PropertyGrid = function(container, config){
36523     config = config || {};
36524     var store = new Roo.grid.PropertyStore(this);
36525     this.store = store;
36526     var cm = new Roo.grid.PropertyColumnModel(this, store);
36527     store.store.sort('name', 'ASC');
36528     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
36529         ds: store.store,
36530         cm: cm,
36531         enableColLock:false,
36532         enableColumnMove:false,
36533         stripeRows:false,
36534         trackMouseOver: false,
36535         clicksToEdit:1
36536     }, config));
36537     this.getGridEl().addClass('x-props-grid');
36538     this.lastEditRow = null;
36539     this.on('columnresize', this.onColumnResize, this);
36540     this.addEvents({
36541          /**
36542              * @event beforepropertychange
36543              * Fires before a property changes (return false to stop?)
36544              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36545              * @param {String} id Record Id
36546              * @param {String} newval New Value
36547          * @param {String} oldval Old Value
36548              */
36549         "beforepropertychange": true,
36550         /**
36551              * @event propertychange
36552              * Fires after a property changes
36553              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36554              * @param {String} id Record Id
36555              * @param {String} newval New Value
36556          * @param {String} oldval Old Value
36557              */
36558         "propertychange": true
36559     });
36560     this.customEditors = this.customEditors || {};
36561 };
36562 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
36563     
36564      /**
36565      * @cfg {Object} customEditors map of colnames=> custom editors.
36566      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
36567      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
36568      * false disables editing of the field.
36569          */
36570     
36571       /**
36572      * @cfg {Object} propertyNames map of property Names to their displayed value
36573          */
36574     
36575     render : function(){
36576         Roo.grid.PropertyGrid.superclass.render.call(this);
36577         this.autoSize.defer(100, this);
36578     },
36579
36580     autoSize : function(){
36581         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
36582         if(this.view){
36583             this.view.fitColumns();
36584         }
36585     },
36586
36587     onColumnResize : function(){
36588         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
36589         this.autoSize();
36590     },
36591     /**
36592      * Sets the data for the Grid
36593      * accepts a Key => Value object of all the elements avaiable.
36594      * @param {Object} data  to appear in grid.
36595      */
36596     setSource : function(source){
36597         this.store.setSource(source);
36598         //this.autoSize();
36599     },
36600     /**
36601      * Gets all the data from the grid.
36602      * @return {Object} data  data stored in grid
36603      */
36604     getSource : function(){
36605         return this.store.getSource();
36606     }
36607 });/*
36608  * Based on:
36609  * Ext JS Library 1.1.1
36610  * Copyright(c) 2006-2007, Ext JS, LLC.
36611  *
36612  * Originally Released Under LGPL - original licence link has changed is not relivant.
36613  *
36614  * Fork - LGPL
36615  * <script type="text/javascript">
36616  */
36617  
36618 /**
36619  * @class Roo.LoadMask
36620  * A simple utility class for generically masking elements while loading data.  If the element being masked has
36621  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
36622  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
36623  * element's UpdateManager load indicator and will be destroyed after the initial load.
36624  * @constructor
36625  * Create a new LoadMask
36626  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
36627  * @param {Object} config The config object
36628  */
36629 Roo.LoadMask = function(el, config){
36630     this.el = Roo.get(el);
36631     Roo.apply(this, config);
36632     if(this.store){
36633         this.store.on('beforeload', this.onBeforeLoad, this);
36634         this.store.on('load', this.onLoad, this);
36635         this.store.on('loadexception', this.onLoad, this);
36636         this.removeMask = false;
36637     }else{
36638         var um = this.el.getUpdateManager();
36639         um.showLoadIndicator = false; // disable the default indicator
36640         um.on('beforeupdate', this.onBeforeLoad, this);
36641         um.on('update', this.onLoad, this);
36642         um.on('failure', this.onLoad, this);
36643         this.removeMask = true;
36644     }
36645 };
36646
36647 Roo.LoadMask.prototype = {
36648     /**
36649      * @cfg {Boolean} removeMask
36650      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
36651      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
36652      */
36653     /**
36654      * @cfg {String} msg
36655      * The text to display in a centered loading message box (defaults to 'Loading...')
36656      */
36657     msg : 'Loading...',
36658     /**
36659      * @cfg {String} msgCls
36660      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
36661      */
36662     msgCls : 'x-mask-loading',
36663
36664     /**
36665      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
36666      * @type Boolean
36667      */
36668     disabled: false,
36669
36670     /**
36671      * Disables the mask to prevent it from being displayed
36672      */
36673     disable : function(){
36674        this.disabled = true;
36675     },
36676
36677     /**
36678      * Enables the mask so that it can be displayed
36679      */
36680     enable : function(){
36681         this.disabled = false;
36682     },
36683
36684     // private
36685     onLoad : function(){
36686         this.el.unmask(this.removeMask);
36687     },
36688
36689     // private
36690     onBeforeLoad : function(){
36691         if(!this.disabled){
36692             this.el.mask(this.msg, this.msgCls);
36693         }
36694     },
36695
36696     // private
36697     destroy : function(){
36698         if(this.store){
36699             this.store.un('beforeload', this.onBeforeLoad, this);
36700             this.store.un('load', this.onLoad, this);
36701             this.store.un('loadexception', this.onLoad, this);
36702         }else{
36703             var um = this.el.getUpdateManager();
36704             um.un('beforeupdate', this.onBeforeLoad, this);
36705             um.un('update', this.onLoad, this);
36706             um.un('failure', this.onLoad, this);
36707         }
36708     }
36709 };/*
36710  * Based on:
36711  * Ext JS Library 1.1.1
36712  * Copyright(c) 2006-2007, Ext JS, LLC.
36713  *
36714  * Originally Released Under LGPL - original licence link has changed is not relivant.
36715  *
36716  * Fork - LGPL
36717  * <script type="text/javascript">
36718  */
36719 Roo.XTemplate = function(){
36720     Roo.XTemplate.superclass.constructor.apply(this, arguments);
36721     var s = this.html;
36722
36723     s = ['<tpl>', s, '</tpl>'].join('');
36724
36725     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
36726
36727     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
36728     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
36729     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
36730     var m, id = 0;
36731     var tpls = [];
36732
36733     while(m = s.match(re)){
36734        var m2 = m[0].match(nameRe);
36735        var m3 = m[0].match(ifRe);
36736        var m4 = m[0].match(execRe);
36737        var exp = null, fn = null, exec = null;
36738        var name = m2 && m2[1] ? m2[1] : '';
36739        if(m3){
36740            exp = m3 && m3[1] ? m3[1] : null;
36741            if(exp){
36742                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
36743            }
36744        }
36745        if(m4){
36746            exp = m4 && m4[1] ? m4[1] : null;
36747            if(exp){
36748                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
36749            }
36750        }
36751        if(name){
36752            switch(name){
36753                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
36754                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
36755                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
36756            }
36757        }
36758        tpls.push({
36759             id: id,
36760             target: name,
36761             exec: exec,
36762             test: fn,
36763             body: m[1]||''
36764         });
36765        s = s.replace(m[0], '{xtpl'+ id + '}');
36766        ++id;
36767     }
36768     for(var i = tpls.length-1; i >= 0; --i){
36769         this.compileTpl(tpls[i]);
36770     }
36771     this.master = tpls[tpls.length-1];
36772     this.tpls = tpls;
36773 };
36774 Roo.extend(Roo.XTemplate, Roo.Template, {
36775
36776     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
36777
36778     applySubTemplate : function(id, values, parent){
36779         var t = this.tpls[id];
36780         if(t.test && !t.test.call(this, values, parent)){
36781             return '';
36782         }
36783         if(t.exec && t.exec.call(this, values, parent)){
36784             return '';
36785         }
36786         var vs = t.target ? t.target.call(this, values, parent) : values;
36787         parent = t.target ? values : parent;
36788         if(t.target && vs instanceof Array){
36789             var buf = [];
36790             for(var i = 0, len = vs.length; i < len; i++){
36791                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
36792             }
36793             return buf.join('');
36794         }
36795         return t.compiled.call(this, vs, parent);
36796     },
36797
36798     compileTpl : function(tpl){
36799         var fm = Roo.util.Format;
36800         var useF = this.disableFormats !== true;
36801         var sep = Roo.isGecko ? "+" : ",";
36802         var fn = function(m, name, format, args){
36803             if(name.substr(0, 4) == 'xtpl'){
36804                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
36805             }
36806             var v;
36807             if(name.indexOf('.') != -1){
36808                 v = name;
36809             }else{
36810                 v = "values['" + name + "']";
36811             }
36812             if(format && useF){
36813                 args = args ? ',' + args : "";
36814                 if(format.substr(0, 5) != "this."){
36815                     format = "fm." + format + '(';
36816                 }else{
36817                     format = 'this.call("'+ format.substr(5) + '", ';
36818                     args = ", values";
36819                 }
36820             }else{
36821                 args= ''; format = "("+v+" === undefined ? '' : ";
36822             }
36823             return "'"+ sep + format + v + args + ")"+sep+"'";
36824         };
36825         var body;
36826         // branched to use + in gecko and [].join() in others
36827         if(Roo.isGecko){
36828             body = "tpl.compiled = function(values, parent){ return '" +
36829                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
36830                     "';};";
36831         }else{
36832             body = ["tpl.compiled = function(values, parent){ return ['"];
36833             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
36834             body.push("'].join('');};");
36835             body = body.join('');
36836         }
36837         /** eval:var:zzzzzzz */
36838         eval(body);
36839         return this;
36840     },
36841
36842     applyTemplate : function(values){
36843         return this.master.compiled.call(this, values, {});
36844         var s = this.subs;
36845     },
36846
36847     apply : function(){
36848         return this.applyTemplate.apply(this, arguments);
36849     },
36850
36851     compile : function(){return this;}
36852 });
36853
36854 Roo.XTemplate.from = function(el){
36855     el = Roo.getDom(el);
36856     return new Roo.XTemplate(el.value || el.innerHTML);
36857 };/*
36858  * Original code for Roojs - LGPL
36859  * <script type="text/javascript">
36860  */
36861  
36862 /**
36863  * @class Roo.XComponent
36864  * A delayed Element creator...
36865  * Or a way to group chunks of interface together.
36866  * 
36867  * Mypart.xyx = new Roo.XComponent({
36868
36869     parent : 'Mypart.xyz', // empty == document.element.!!
36870     order : '001',
36871     name : 'xxxx'
36872     region : 'xxxx'
36873     disabled : function() {} 
36874      
36875     tree : function() { // return an tree of xtype declared components
36876         var MODULE = this;
36877         return 
36878         {
36879             xtype : 'NestedLayoutPanel',
36880             // technicall
36881         }
36882      ]
36883  *})
36884  *
36885  *
36886  * It can be used to build a big heiracy, with parent etc.
36887  * or you can just use this to render a single compoent to a dom element
36888  * MYPART.render(Roo.Element | String(id) | dom_element )
36889  * 
36890  * @extends Roo.util.Observable
36891  * @constructor
36892  * @param cfg {Object} configuration of component
36893  * 
36894  */
36895 Roo.XComponent = function(cfg) {
36896     Roo.apply(this, cfg);
36897     this.addEvents({ 
36898         /**
36899              * @event built
36900              * Fires when this the componnt is built
36901              * @param {Roo.XComponent} c the component
36902              */
36903         'built' : true,
36904         /**
36905              * @event buildcomplete
36906              * Fires on the top level element when all elements have been built
36907              * @param {Roo.XComponent} c the top level component.
36908          */
36909         'buildcomplete' : true
36910         
36911     });
36912     this.region = this.region || 'center'; // default..
36913     Roo.XComponent.register(this);
36914     this.modules = false;
36915     this.el = false; // where the layout goes..
36916     
36917     
36918 }
36919 Roo.extend(Roo.XComponent, Roo.util.Observable, {
36920     /**
36921      * @property el
36922      * The created element (with Roo.factory())
36923      * @type {Roo.Layout}
36924      */
36925     el  : false,
36926     
36927     /**
36928      * @property el
36929      * for BC  - use el in new code
36930      * @type {Roo.Layout}
36931      */
36932     panel : false,
36933     
36934     /**
36935      * @property layout
36936      * for BC  - use el in new code
36937      * @type {Roo.Layout}
36938      */
36939     layout : false,
36940     
36941      /**
36942      * @cfg {Function|boolean} disabled
36943      * If this module is disabled by some rule, return true from the funtion
36944      */
36945     disabled : false,
36946     
36947     /**
36948      * @cfg {String} parent 
36949      * Name of parent element which it get xtype added to..
36950      */
36951     parent: false,
36952     
36953     /**
36954      * @cfg {String} order
36955      * Used to set the order in which elements are created (usefull for multiple tabs)
36956      */
36957     
36958     order : false,
36959     /**
36960      * @cfg {String} name
36961      * String to display while loading.
36962      */
36963     name : false,
36964     /**
36965      * @cfg {String} region
36966      * Region to render component to (defaults to center)
36967      */
36968     region : 'center',
36969     
36970     /**
36971      * @cfg {Array} items
36972      * A single item array - the first element is the root of the tree..
36973      * It's done this way to stay compatible with the Xtype system...
36974      */
36975     items : false,
36976     
36977     
36978      /**
36979      * render
36980      * render element to dom or tree
36981      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
36982      */
36983     
36984     render : function(el)
36985     {
36986         
36987         el = el || false;
36988         var hp = this.parent ? 1 : 0;
36989         
36990         if (!el && typeof(this.parent) == 'string' && this.parent[0] == '#') {
36991             // if parent is a '#.....' string, then let's use that..
36992             var ename = this.parent.substr(1)
36993             this.parent = false;
36994             el = Roo.get(ename);
36995             if (!el) {
36996                 Roo.log("Warning - element can not be found :#" + ename );
36997                 return;
36998             }
36999         }
37000         
37001         
37002         if (!this.parent) {
37003             
37004             el = el ? Roo.get(el) : false;
37005             
37006             // it's a top level one..
37007             this.parent =  {
37008                 el : new Roo.BorderLayout(el || document.body, {
37009                 
37010                      center: {
37011                          titlebar: false,
37012                          autoScroll:false,
37013                          closeOnTab: true,
37014                          tabPosition: 'top',
37015                           //resizeTabs: true,
37016                          alwaysShowTabs: el && hp? false :  true,
37017                          hideTabs: el || !hp ? true :  false,
37018                          minTabWidth: 140
37019                      }
37020                  })
37021             }
37022         }
37023         
37024         
37025             
37026         var tree = this.tree();
37027         tree.region = tree.region || this.region;
37028         this.el = this.parent.el.addxtype(tree);
37029         this.fireEvent('built', this);
37030         
37031         this.panel = this.el;
37032         this.layout = this.panel.layout;    
37033          
37034     }
37035     
37036 });
37037
37038 Roo.apply(Roo.XComponent, {
37039     
37040     /**
37041      * @property  buildCompleted
37042      * True when the builder has completed building the interface.
37043      * @type Boolean
37044      */
37045     buildCompleted : false,
37046      
37047     /**
37048      * @property  topModule
37049      * the upper most module - uses document.element as it's constructor.
37050      * @type Object
37051      */
37052      
37053     topModule  : false,
37054       
37055     /**
37056      * @property  modules
37057      * array of modules to be created by registration system.
37058      * @type {Array} of Roo.XComponent
37059      */
37060     
37061     modules : [],
37062     /**
37063      * @property  elmodules
37064      * array of modules to be created by which use #ID 
37065      * @type {Array} of Roo.XComponent
37066      */
37067      
37068     elmodules : [],
37069
37070     
37071     /**
37072      * Register components to be built later.
37073      *
37074      * This solves the following issues
37075      * - Building is not done on page load, but after an authentication process has occured.
37076      * - Interface elements are registered on page load
37077      * - Parent Interface elements may not be loaded before child, so this handles that..
37078      * 
37079      *
37080      * example:
37081      * 
37082      * MyApp.register({
37083           order : '000001',
37084           module : 'Pman.Tab.projectMgr',
37085           region : 'center',
37086           parent : 'Pman.layout',
37087           disabled : false,  // or use a function..
37088         })
37089      
37090      * * @param {Object} details about module
37091      */
37092     register : function(obj) {
37093         this.modules.push(obj);
37094          
37095     },
37096     /**
37097      * convert a string to an object..
37098      * eg. 'AAA.BBB' -> finds AAA.BBB
37099
37100      */
37101     
37102     toObject : function(str)
37103     {
37104         if (!str || typeof(str) == 'object') {
37105             return str;
37106         }
37107         if (str[0]=='#') {
37108             return str;
37109         }
37110
37111         var ar = str.split('.');
37112         var rt, o;
37113         rt = ar.shift();
37114             /** eval:var:o */
37115         eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
37116         if (o === false) {
37117             throw "Module not found : " + str;
37118         }
37119         Roo.each(ar, function(e) {
37120             if (typeof(o[e]) == 'undefined') {
37121                 throw "Module not found : " + str;
37122             }
37123             o = o[e];
37124         });
37125         
37126         return o;
37127         
37128     },
37129     
37130     
37131     /**
37132      * move modules into their correct place in the tree..
37133      * 
37134      */
37135     preBuild : function ()
37136     {
37137         var _t = this;
37138         Roo.each(this.modules , function (obj)
37139         {
37140             var opar = obj.parent;
37141             obj.parent = this.toObject(opar);
37142             
37143             if (!obj.parent) {
37144                 this.topModule = obj;
37145                 return;
37146             }
37147             if (typeof(obj.parent) == 'string') {
37148                 this.elmodules.push(obj);
37149                 return;
37150             }
37151             if (obj.parent.constructor != Roo.XComponent) {
37152                 Roo.log("Object Parent is not instance of XComponent:" + obj.name)
37153             }
37154             if (!obj.parent.modules) {
37155                 obj.parent.modules = new Roo.util.MixedCollection(false, 
37156                     function(o) { return o.order + '' }
37157                 );
37158             }
37159             
37160             obj.parent.modules.add(obj);
37161         }, this);
37162     },
37163     
37164      /**
37165      * make a list of modules to build.
37166      * @return {Array} list of modules. 
37167      */ 
37168     
37169     buildOrder : function()
37170     {
37171         var _this = this;
37172         var cmp = function(a,b) {   
37173             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
37174         };
37175         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
37176             throw "No top level modules to build";
37177         }
37178         
37179         // make a flat list in order of modules to build.
37180         var mods = this.topModule ? [ this.topModule ] : [];
37181         Roo.each(this.elmodules,function(e) { mods.push(e) });
37182
37183         
37184         // add modules to their parents..
37185         var addMod = function(m) {
37186            // Roo.debug && Roo.log(m.modKey);
37187             
37188             mods.push(m);
37189             if (m.modules) {
37190                 m.modules.keySort('ASC',  cmp );
37191                 m.modules.each(addMod);
37192             }
37193             // not sure if this is used any more..
37194             if (m.finalize) {
37195                 m.finalize.name = m.name + " (clean up) ";
37196                 mods.push(m.finalize);
37197             }
37198             
37199         }
37200         if (this.topModule) { 
37201             this.topModule.modules.keySort('ASC',  cmp );
37202             this.topModule.modules.each(addMod);
37203         }
37204         return mods;
37205     },
37206     
37207      /**
37208      * Build the registered modules.
37209      * @param {Object} parent element.
37210      * @param {Function} optional method to call after module has been added.
37211      * 
37212      */ 
37213    
37214     build : function() 
37215     {
37216         
37217         this.preBuild();
37218         var mods = this.buildOrder();
37219       
37220         //this.allmods = mods;
37221         //Roo.debug && Roo.log(mods);
37222         //return;
37223         if (!mods.length) { // should not happen
37224             throw "NO modules!!!";
37225         }
37226         
37227         
37228         
37229         // flash it up as modal - so we store the mask!?
37230         Roo.MessageBox.show({ title: 'loading' });
37231         Roo.MessageBox.show({
37232            title: "Please wait...",
37233            msg: "Building Interface...",
37234            width:450,
37235            progress:true,
37236            closable:false,
37237            modal: false
37238           
37239         });
37240         var total = mods.length;
37241         
37242         var _this = this;
37243         var progressRun = function() {
37244             if (!mods.length) {
37245                 Roo.debug && Roo.log('hide?');
37246                 Roo.MessageBox.hide();
37247                 if (_this.topModule) { 
37248                     _this.topModule.fireEvent('buildcomplete', _this.topModule);
37249                 }
37250                 // THE END...
37251                 return false;   
37252             }
37253             
37254             var m = mods.shift();
37255             
37256             
37257             Roo.debug && Roo.log(m);
37258             // not sure if this is supported any more.. - modules that are are just function
37259             if (typeof(m) == 'function') { 
37260                 m.call(this);
37261                 return progressRun.defer(10, _this);
37262             } 
37263             
37264             
37265             
37266             Roo.MessageBox.updateProgress(
37267                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
37268                     " of " + total + 
37269                     (m.name ? (' - ' + m.name) : '')
37270                     );
37271             
37272          
37273             // is the module disabled?
37274             var disabled = (typeof(m.disabled) == 'function') ?
37275                 m.disabled.call(m.module.disabled) : m.disabled;    
37276             
37277             
37278             if (disabled) {
37279                 return progressRun(); // we do not update the display!
37280             }
37281             
37282             // now build 
37283             
37284             m.render();
37285             // it's 10 on top level, and 1 on others??? why...
37286             return progressRun.defer(10, _this);
37287              
37288         }
37289         progressRun.defer(1, _this);
37290      
37291         
37292         
37293     }
37294     
37295      
37296    
37297     
37298     
37299 });
37300  //<script type="text/javascript">
37301
37302
37303 /**
37304  * @class Roo.Login
37305  * @extends Roo.LayoutDialog
37306  * A generic Login Dialog..... - only one needed in theory!?!?
37307  *
37308  * Fires XComponent builder on success...
37309  * 
37310  * Sends 
37311  *    username,password, lang = for login actions.
37312  *    check = 1 for periodic checking that sesion is valid.
37313  *    passwordRequest = email request password
37314  *    logout = 1 = to logout
37315  * 
37316  * Affects: (this id="????" elements)
37317  *   loading  (removed) (used to indicate application is loading)
37318  *   loading-mask (hides) (used to hide application when it's building loading)
37319  *   
37320  * 
37321  * Usage: 
37322  *    
37323  * 
37324  * Myapp.login = Roo.Login({
37325      url: xxxx,
37326    
37327      realm : 'Myapp', 
37328      
37329      
37330      method : 'POST',
37331      
37332      
37333      * 
37334  })
37335  * 
37336  * 
37337  * 
37338  **/
37339  
37340 Roo.Login = function(cfg)
37341 {
37342     this.addEvents({
37343         'refreshed' : true
37344     });
37345     
37346     Roo.apply(this,cfg);
37347     
37348     Roo.onReady(function() {
37349         this.onLoad();
37350     }, this);
37351     // call parent..
37352     
37353    
37354     Roo.Login.superclass.constructor.call(this, this);
37355     //this.addxtype(this.items[0]);
37356     
37357     
37358 }
37359
37360
37361 Roo.extend(Roo.Login, Roo.LayoutDialog, {
37362     
37363     /**
37364      * @cfg {String} method
37365      * Method used to query for login details.
37366      */
37367     
37368     method : 'POST',
37369     /**
37370      * @cfg {String} url
37371      * URL to query login data. - eg. baseURL + '/Login.php'
37372      */
37373     url : '',
37374     
37375     /**
37376      * @property user
37377      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
37378      * @type {Object} 
37379      */
37380     user : false,
37381     /**
37382      * @property checkFails
37383      * Number of times we have attempted to get authentication check, and failed.
37384      * @type {Number} 
37385      */
37386     checkFails : 0,
37387       /**
37388      * @property intervalID
37389      * The window interval that does the constant login checking.
37390      * @type {Number} 
37391      */
37392     intervalID : 0,
37393     
37394     
37395     onLoad : function() // called on page load...
37396     {
37397         // load 
37398          
37399         if (Roo.get('loading')) { // clear any loading indicator..
37400             Roo.get('loading').remove();
37401         }
37402         
37403         //this.switchLang('en'); // set the language to english..
37404        
37405         this.check({
37406             success:  function(response, opts)  {  // check successfull...
37407             
37408                 var res = this.processResponse(response);
37409                 this.checkFails =0;
37410                 if (!res.success) { // error!
37411                     this.checkFails = 5;
37412                     //console.log('call failure');
37413                     return this.failure(response,opts);
37414                 }
37415                 
37416                 if (!res.data.id) { // id=0 == login failure.
37417                     return this.show();
37418                 }
37419                 
37420                               
37421                         //console.log(success);
37422                 this.fillAuth(res.data);   
37423                 this.checkFails =0;
37424                 Roo.XComponent.build();
37425             },
37426             failure : this.show
37427         });
37428         
37429     }, 
37430     
37431     
37432     check: function(cfg) // called every so often to refresh cookie etc..
37433     {
37434         if (cfg.again) { // could be undefined..
37435             this.checkFails++;
37436         } else {
37437             this.checkFails = 0;
37438         }
37439         var _this = this;
37440         if (this.sending) {
37441             if ( this.checkFails > 4) {
37442                 Roo.MessageBox.alert("Error",  
37443                     "Error getting authentication status. - try reloading, or wait a while", function() {
37444                         _this.sending = false;
37445                     }); 
37446                 return;
37447             }
37448             cfg.again = true;
37449             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
37450             return;
37451         }
37452         this.sending = true;
37453         
37454         Roo.Ajax.request({  
37455             url: this.url,
37456             params: {
37457                 getAuthUser: true
37458             },  
37459             method: this.method,
37460             success:  cfg.success || this.success,
37461             failure : cfg.failure || this.failure,
37462             scope : this,
37463             callCfg : cfg
37464               
37465         });  
37466     }, 
37467     
37468     
37469     logout: function()
37470     {
37471         window.onbeforeunload = function() { }; // false does not work for IE..
37472         this.user = false;
37473         var _this = this;
37474         
37475         Roo.Ajax.request({  
37476             url: this.url,
37477             params: {
37478                 logout: 1
37479             },  
37480             method: 'GET',
37481             failure : function() {
37482                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
37483                     document.location = document.location.toString() + '?ts=' + Math.random();
37484                 });
37485                 
37486             },
37487             success : function() {
37488                 _this.user = false;
37489                 this.checkFails =0;
37490                 // fixme..
37491                 document.location = document.location.toString() + '?ts=' + Math.random();
37492             }
37493               
37494               
37495         }); 
37496     },
37497     
37498     processResponse : function (response)
37499     {
37500         var res = '';
37501         try {
37502             res = Roo.decode(response.responseText);
37503             // oops...
37504             if (typeof(res) != 'object') {
37505                 res = { success : false, errorMsg : res, errors : true };
37506             }
37507             if (typeof(res.success) == 'undefined') {
37508                 res.success = false;
37509             }
37510             
37511         } catch(e) {
37512             res = { success : false,  errorMsg : response.responseText, errors : true };
37513         }
37514         return res;
37515     },
37516     
37517     success : function(response, opts)  // check successfull...
37518     {  
37519         this.sending = false;
37520         var res = this.processResponse(response);
37521         if (!res.success) {
37522             return this.failure(response, opts);
37523         }
37524         if (!res.data || !res.data.id) {
37525             return this.failure(response,opts);
37526         }
37527         //console.log(res);
37528         this.fillAuth(res.data);
37529         
37530         this.checkFails =0;
37531         
37532     },
37533     
37534     
37535     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
37536     {
37537         this.authUser = -1;
37538         this.sending = false;
37539         var res = this.processResponse(response);
37540         //console.log(res);
37541         if ( this.checkFails > 2) {
37542         
37543             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
37544                 "Error getting authentication status. - try reloading"); 
37545             return;
37546         }
37547         opts.callCfg.again = true;
37548         this.check.defer(1000, this, [ opts.callCfg ]);
37549         return;  
37550     },
37551     
37552     
37553     
37554     fillAuth: function(au) {
37555         this.startAuthCheck();
37556         this.authUserId = au.id;
37557         this.authUser = au;
37558         this.lastChecked = new Date();
37559         this.fireEvent('refreshed', au);
37560         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
37561         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
37562         au.lang = au.lang || 'en';
37563         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
37564         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
37565         this.switchLang(au.lang );
37566         
37567      
37568         // open system... - -on setyp..
37569         if (this.authUserId  < 0) {
37570             Roo.MessageBox.alert("Warning", 
37571                 "This is an open system - please set up a admin user with a password.");  
37572         }
37573          
37574         //Pman.onload(); // which should do nothing if it's a re-auth result...
37575         
37576              
37577     },
37578     
37579     startAuthCheck : function() // starter for timeout checking..
37580     {
37581         if (this.intervalID) { // timer already in place...
37582             return false;
37583         }
37584         var _this = this;
37585         this.intervalID =  window.setInterval(function() {
37586               _this.check(false);
37587             }, 120000); // every 120 secs = 2mins..
37588         
37589         
37590     },
37591          
37592     
37593     switchLang : function (lang) 
37594     {
37595         _T = typeof(_T) == 'undefined' ? false : _T;
37596           if (!_T || !lang.length) {
37597             return;
37598         }
37599         
37600         if (!_T && lang != 'en') {
37601             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
37602             return;
37603         }
37604         
37605         if (typeof(_T.en) == 'undefined') {
37606             _T.en = {};
37607             Roo.apply(_T.en, _T);
37608         }
37609         
37610         if (typeof(_T[lang]) == 'undefined') {
37611             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
37612             return;
37613         }
37614         
37615         
37616         Roo.apply(_T, _T[lang]);
37617         // just need to set the text values for everything...
37618         var _this = this;
37619         /* this will not work ...
37620         if (this.form) { 
37621             
37622                
37623             function formLabel(name, val) {
37624                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
37625             }
37626             
37627             formLabel('password', "Password"+':');
37628             formLabel('username', "Email Address"+':');
37629             formLabel('lang', "Language"+':');
37630             this.dialog.setTitle("Login");
37631             this.dialog.buttons[0].setText("Forgot Password");
37632             this.dialog.buttons[1].setText("Login");
37633         }
37634         */
37635         
37636         
37637     },
37638     
37639     
37640     title: "Login",
37641     modal: true,
37642     width:  350,
37643     //height: 230,
37644     height: 180,
37645     shadow: true,
37646     minWidth:200,
37647     minHeight:180,
37648     //proxyDrag: true,
37649     closable: false,
37650     draggable: false,
37651     collapsible: false,
37652     resizable: false,
37653     center: {  // needed??
37654         autoScroll:false,
37655         titlebar: false,
37656        // tabPosition: 'top',
37657         hideTabs: true,
37658         closeOnTab: true,
37659         alwaysShowTabs: false
37660     } ,
37661     listeners : {
37662         
37663         show  : function(dlg)
37664         {
37665             //console.log(this);
37666             this.form = this.layout.getRegion('center').activePanel.form;
37667             this.form.dialog = dlg;
37668             this.buttons[0].form = this.form;
37669             this.buttons[0].dialog = dlg;
37670             this.buttons[1].form = this.form;
37671             this.buttons[1].dialog = dlg;
37672            
37673            //this.resizeToLogo.defer(1000,this);
37674             // this is all related to resizing for logos..
37675             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
37676            //// if (!sz) {
37677              //   this.resizeToLogo.defer(1000,this);
37678              //   return;
37679            // }
37680             //var w = Ext.lib.Dom.getViewWidth() - 100;
37681             //var h = Ext.lib.Dom.getViewHeight() - 100;
37682             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
37683             //this.center();
37684             if (this.disabled) {
37685                 this.hide();
37686                 return;
37687             }
37688             
37689             if (this.user.id < 0) { // used for inital setup situations.
37690                 return;
37691             }
37692             
37693             if (this.intervalID) {
37694                 // remove the timer
37695                 window.clearInterval(this.intervalID);
37696                 this.intervalID = false;
37697             }
37698             
37699             
37700             if (Roo.get('loading')) {
37701                 Roo.get('loading').remove();
37702             }
37703             if (Roo.get('loading-mask')) {
37704                 Roo.get('loading-mask').hide();
37705             }
37706             
37707             //incomming._node = tnode;
37708             this.form.reset();
37709             //this.dialog.modal = !modal;
37710             //this.dialog.show();
37711             this.el.unmask(); 
37712             
37713             
37714             this.form.setValues({
37715                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
37716                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
37717             });
37718             
37719             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
37720             if (this.form.findField('username').getValue().length > 0 ){
37721                 this.form.findField('password').focus();
37722             } else {
37723                this.form.findField('username').focus();
37724             }
37725     
37726         }
37727     },
37728     items : [
37729          {
37730        
37731             xtype : 'ContentPanel',
37732             xns : Roo,
37733             region: 'center',
37734             fitToFrame : true,
37735             
37736             items : [
37737     
37738                 {
37739                
37740                     xtype : 'Form',
37741                     xns : Roo.form,
37742                     labelWidth: 100,
37743                     style : 'margin: 10px;',
37744                     
37745                     listeners : {
37746                         actionfailed : function(f, act) {
37747                             // form can return { errors: .... }
37748                                 
37749                             //act.result.errors // invalid form element list...
37750                             //act.result.errorMsg// invalid form element list...
37751                             
37752                             this.dialog.el.unmask();
37753                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
37754                                         "Login failed - communication error - try again.");
37755                                       
37756                         },
37757                         actioncomplete: function(re, act) {
37758                              
37759                             Roo.state.Manager.set(
37760                                 this.dialog.realm + '.username',  
37761                                     this.findField('username').getValue()
37762                             );
37763                             Roo.state.Manager.set(
37764                                 this.dialog.realm + '.lang',  
37765                                 this.findField('lang').getValue() 
37766                             );
37767                             
37768                             this.dialog.fillAuth(act.result.data);
37769                               
37770                             this.dialog.hide();
37771                             
37772                             if (Roo.get('loading-mask')) {
37773                                 Roo.get('loading-mask').show();
37774                             }
37775                             Roo.XComponent.build();
37776                             
37777                              
37778                             
37779                         }
37780                     },
37781                     items : [
37782                         {
37783                             xtype : 'TextField',
37784                             xns : Roo.form,
37785                             fieldLabel: "Email Address",
37786                             name: 'username',
37787                             width:200,
37788                             autoCreate : {tag: "input", type: "text", size: "20"}
37789                         },
37790                         {
37791                             xtype : 'TextField',
37792                             xns : Roo.form,
37793                             fieldLabel: "Password",
37794                             inputType: 'password',
37795                             name: 'password',
37796                             width:200,
37797                             autoCreate : {tag: "input", type: "text", size: "20"},
37798                             listeners : {
37799                                 specialkey : function(e,ev) {
37800                                     if (ev.keyCode == 13) {
37801                                         this.form.dialog.el.mask("Logging in");
37802                                         this.form.doAction('submit', {
37803                                             url: this.form.dialog.url,
37804                                             method: this.form.dialog.method
37805                                         });
37806                                     }
37807                                 }
37808                             }  
37809                         },
37810                         {
37811                             xtype : 'ComboBox',
37812                             xns : Roo.form,
37813                             fieldLabel: "Language",
37814                             name : 'langdisp',
37815                             store: {
37816                                 xtype : 'SimpleStore',
37817                                 fields: ['lang', 'ldisp'],
37818                                 data : [
37819                                     [ 'en', 'English' ],
37820                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
37821                                     [ 'zh_CN', '\u7C21\u4E2D' ]
37822                                 ]
37823                             },
37824                             
37825                             valueField : 'lang',
37826                             hiddenName:  'lang',
37827                             width: 200,
37828                             displayField:'ldisp',
37829                             typeAhead: false,
37830                             editable: false,
37831                             mode: 'local',
37832                             triggerAction: 'all',
37833                             emptyText:'Select a Language...',
37834                             selectOnFocus:true,
37835                             listeners : {
37836                                 select :  function(cb, rec, ix) {
37837                                     this.form.switchLang(rec.data.lang);
37838                                 }
37839                             }
37840                         
37841                         }
37842                     ]
37843                 }
37844                   
37845                 
37846             ]
37847         }
37848     ],
37849     buttons : [
37850         {
37851             xtype : 'Button',
37852             xns : 'Roo',
37853             text : "Forgot Password",
37854             listeners : {
37855                 click : function() {
37856                     //console.log(this);
37857                     var n = this.form.findField('username').getValue();
37858                     if (!n.length) {
37859                         Roo.MessageBox.alert("Error", "Fill in your email address");
37860                         return;
37861                     }
37862                     Roo.Ajax.request({
37863                         url: this.dialog.url,
37864                         params: {
37865                             passwordRequest: n
37866                         },
37867                         method: this.dialog.method,
37868                         success:  function(response, opts)  {  // check successfull...
37869                         
37870                             var res = this.dialog.processResponse(response);
37871                             if (!res.success) { // error!
37872                                Roo.MessageBox.alert("Error" ,
37873                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
37874                                return;
37875                             }
37876                             Roo.MessageBox.alert("Notice" ,
37877                                 "Please check you email for the Password Reset message");
37878                         },
37879                         failure : function() {
37880                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
37881                         }
37882                         
37883                     });
37884                 }
37885             }
37886         },
37887         {
37888             xtype : 'Button',
37889             xns : 'Roo',
37890             text : "Login",
37891             listeners : {
37892                 
37893                 click : function () {
37894                         
37895                     this.dialog.el.mask("Logging in");
37896                     this.form.doAction('submit', {
37897                             url: this.dialog.url,
37898                             method: this.dialog.method
37899                     });
37900                 }
37901             }
37902         }
37903     ]
37904   
37905   
37906 })
37907  
37908
37909
37910